C++对象的虚函数调用

3

通过Bruce Eckel的《Thinking in C ++》学习C ++。在第30个练习中卡住了。如下所示:

如果对按值传递的对象进行的函数调用不是早期绑定,则虚拟调用可能会访问不存在的部分。这种情况可能吗?编写一些代码来强制进行虚拟调用,看看是否会导致崩溃。为了解释行为,请检查按值传递对象时会发生什么。

我可以理解为对象调用虚拟函数的结果,但是我不知道如何强制编译器这样做,而不调用适当的构造函数。

是否有一种方法可以将一个对象视为另一个对象,而不调用适当的构造函数或运算符(用于类型转换)?

2个回答

2

Bruce试图说明对象切割,这是在将多态对象按值传递时出现的情况。

以下是实现方法:

#include <iostream>
using namespace std;
struct hello {
    virtual void say() { cout << "hello" << endl; }
};
struct world : public hello {
    virtual void say() { cout << "world" << endl; }
};
void force(hello h) {
    h.say();
}
int main() {
    world w;
    w.say();
    force(w);
    return 0;
}

这段代码输出内容如下 (点击此处查看)。
world
hello

尽管您预期类型为world的对象会“说”world而不是hello,但C++编译器聪明地注意到w按值传递给hello,因此调整了vtable以避免在派生类中调用方法。
奖励练习是为了测试您是否理解按引用传递:您能否修改我的代码,以便它打印worldworld?您可以插入一个字符。

虽然我从来没有理解为什么有人会期望调用worldsay()方法,但这是真的。h是一个复制的、完全不同类型的对象。 - Alex
程序员们如果之前接触过其他多态语言,看到打印出来的 hello 会感到惊讶,因为在 Java、C# 或 Objective-C 中不会发生对象切片(这是一件好事;对象切片行为是试图修复本应该在第一时间就被禁止的东西)。 - Sergey Kalinichenko
1
@dasblinkenlight:另外,对象切片是满足Liskov替换原则的一种勇敢尝试——因为可以使用“Base”来初始化“Base”,所以也应该可以使用“Derived”来初始化“Base”,因为“Derived”是“Base”。我并不一定声称这是将LSP应用于值类型的最佳可行方法,但就是这样。如果您选择编写一个接受“Base”的“Base”构造函数,则在Java中会发生等效的事情,但通常您选择这样做,而是选择进行“clone()”。 - Steve Jessop
哦,在我的LSP引起的观点中,如果有任何错误,那么首先错误的是把Base(const Base&)称为复制构造函数,因为它不一定会复制其参数。它只复制其参数的基类子对象。如果我们称之为“切片构造”,“切片赋值”和“按切片传递”,那么在Java程序员陷入困境之前,我们可能会帮助他们意识到C++中发生了什么有趣的事情;-) - Steve Jessop

0

虽然对象不能在没有适当运算符的情况下从一种类型转换为另一种类型,但您可以使用指针并使用 static_cast<>、dynamic_cast<> 或 reinterpret_cast<> 进行强制转换。


这个练习是关于为对象调用虚函数,而不是指针或引用。 - user1641854
2
@user1641854:你可以使用指针或对象的引用来对该对象进行虚函数调用。事实上,这是唯一的方法。 - Steve Jessop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接