我知道在Base类的构造函数中 - 当调用虚方法时 - 调用Base方法而不是派生 - 请参阅Calling virtual functions inside constructors。
我的问题与此主题有关。我只是想知道如果我在Derived类构造函数中调用虚方法会发生什么 - 但是在构造Base部分之前。我的意思是调用虚方法来评估Base类的构造函数参数,参见代码:
class Base {
public:
Base(const char* name) : name(name) {
cout << "Base():" << name << endl;
}
virtual const char* getName() {
cout << "Base::getName()" << endl;
return "Base";
}
protected:
const char* name;
};
class Derived : public Base {
public:
Derived() : Base(getName()) {
cout << "Derived():" << name << endl;
}
virtual const char* getName() {
cout << "Derived::getName()" << endl;
return "Derived";
}
};
int main() {
Derived d;
}
编译器g ++(4.3.x-4.5x版本)输出为:
Derived::getName()
Base():Derived
Derived():Derived
但是我希望:
Base::getName()
Base():Base
Derived():Base
这看起来不错 - 但请考虑这个产生segmentation fault
:
class Derived : public Base {
public:
Derived() : Base(getName()), name(new string("Derived")) {
cout << "Derived():" << Base::name << endl;
}
virtual const char* getName() {
cout << "Derived::getName()" << endl;
return name->c_str();
}
private:
string* name;
};
请回答:这是正确的g ++行为吗? C ++标准对此有何看法?也许这是未定义的行为?
[UPDATE1] 我考虑到罗伯特和奥利的答案 - 我改变了我的第一个例子。然后getName()被称为“虚拟” - 它会产生分段错误。请回答我对这部分的问题。
const char* virtualGetName(Base* basePtr)
{
return basePtr->getName();
}
class Derived : public Base {
public:
Derived() : Base(virtualGetName(this)) {
cout << "Derived():" << Base::name << endl;
}
virtual const char* getName() {
cout << "Derived::getName()" << endl;
return "Derived";
}
};
答案 0 :(得分:11)
您的所有示例都显示未定义的行为。 C ++语言标准(C ++11§12.6.2/ 13):
可以为正在构建的对象调用成员函数(包括虚拟成员函数)。同样,正在构建的对象可以是
typeid
运算符或dynamic_cast
的操作数。但是,如果在 ctor-initializer (或直接或间接从 ctor-initializer 中调用的函数)中执行这些操作,那么基类的mem-initializers 已经完成,操作的结果是未定义的。
您正在从getName()
类构造函数的初始化列表( ctor-initializer )中调用成员函数Derived
。此成员函数调用必须在Base
的初始化程序完成之前发生( mem-initializer 表示Base
),因为它是初始化程序本身的参数。
因此,行为未定义。
通常,Never Call Virtual Functions during Construction or Destruction。
答案 1 :(得分:1)
我只是想知道如果我在Derived类构造函数中调用虚方法会发生什么 - 但是在构建Base部分之前会发生什么。
看起来你可能正在这样做,但你不是。
在第一个示例中,Derived::getName()
不依赖this
,因此方法调用有效。在第二个示例中,Derived::getName()
确实取决于this
。由于尚未设置this->name
,因此它指向未定义的位置并为您提供段错误。
this->name
尚未设置,因为Derived
构造函数所做的第一件事就是调用Base
构造函数。如果指定要传递给Base
构造函数的参数,则首先处理它们。然后它通过调用它们的构造函数来实例化类的成员变量。初始化列表可用于将参数传递给那些构造函数,但它不能更改它们被调用的顺序。此步骤是name
初始化的位置。最后,执行Derived
构造函数的主体。