在 C++ 中,当从构造函数中调用虚函数时,它的行为不像虚函数。
我认为第一次遇到这种行为的每个人都感到惊讶,但第二次认为这是有道理的:
只要派生的构造函数还没有被执行,对象就不是还是一个派生的实例。
那么如何调用派生函数呢?前提条件还没来得及设置。示例:
class base {
public:
base()
{
std::cout << "foo is " << foo() << std::endl;
}
virtual int foo() { return 42; }
};
class derived : public base {
int* ptr_;
public:
derived(int i) : ptr_(new int(i*i)) { }
// The following cannot be called before derived::derived due to how C++ behaves,
// if it was possible... Kaboom!
virtual int foo() { return *ptr_; }
};
Java 和 .NET 完全一样,但他们选择了另一种方式,这可能是最小意外原则的唯一原因?
你认为哪个是正确的选择?
最佳答案
语言定义对象生命周期的方式存在根本差异。在 Java 和 .Net 中,对象成员在任何构造函数运行之前被初始化为零/空,此时对象生命周期开始。所以当你进入构造函数时,你已经得到了一个初始化的对象。
在 C++ 中,对象的生命周期仅在构造函数完成时开始(尽管成员变量和基类在它开始之前已完全构造)。这解释了调用虚函数时的行为,以及为什么在构造函数的主体中存在异常时不运行析构函数。
Java/.Net 中对象生命周期定义的问题在于,当对象已初始化但构造函数尚未运行时,很难确保对象始终满足其不变量,而不必考虑特殊情况。 C++ 定义的问题在于,您有一段奇怪的时期,对象处于不确定状态且未完全构造。
关于java - 构造函数中的虚函数,为什么语言不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36832/