4

我知道在 Base 类的构造函数中 - 调用虚拟方法时 - Base 方法被调用,而不是派生 - 请参阅Calling virtual functions inside constructors

我的问题与这个主题有关。我只是想知道如果我在 Derived 类构造函数中调用虚方法会发生什么 - 但在构造 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++ 标准对此有何规定?也许这是未定义的行为?

[更新1] 我考虑了罗伯特和奥利的答案——我改变了我的第一个例子。然后它 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";
  }
};
4

2 回答 2

11

您的所有示例都表现出未定义的行为。C++ 语言标准规定(C++11 §12.6.2/13):

可以为正在构建的对象调用成员函数(包括虚成员函数)。类似地,正在构建的对象可以是运算typeid符或 a的操作数dynamic_cast

但是,如果这些操作在基类的所有mem-initializer完成之前在ctor-initializer(或直接或间接从ctor-initializer调用的函数中)执行,则操作的结果是未定义的。

您正在从类构造函数getName()的初始化列表(ctor-initializer)中调用成员函数。Derived这个成员函数调用必须在初始化器Base完成(mem-initializer for Base)之前发生,因为它是初始化器本身的参数。

因此,行为是未定义的。

作为一项规则,永远不要在构造或破坏期间调用虚函数

于 2012-07-04T21:38:24.170 回答
1

我只是想知道如果我在 Derived 类构造函数中调用虚方法会发生什么 - 但在构造 Base 部分之前。

看起来你正在这样做,但事实并非如此。

在您的第一个示例中,Derived::getName()不依赖于this,因此方法调用有效。在您的第二个示例中,Derived::getName()确实取决于this. 由于this->name尚未设置,它指向一个未定义的位置并给你一个段错误。

this->name尚未设置,因为构造函数所做的第一件事Derived就是调用Base构造函数。如果您指定要传递给Base构造函数的参数,它会首先处理它们。然后它通过调用它们的构造函数来实例化类的成员变量。初始化列表可用于将参数传递给这些构造函数,但它不能更改调用它们的顺序。这一步是name初始化的地方。最后,Derived执行构造函数的主体。

于 2012-07-04T21:26:29.610 回答