从基础 class 构造函数调用派生 class 的成员函数

Calling a member function of a derived class from the base class constructor

假设我有

struct C {
   C() { init(); };
   void init() { cout << "C" << endl; };
};

struct D : public C {
   D() : C() { };
   void init() { cout << "D" << endl; }
};

D();

为什么我打印 "C"?如何改变这种行为(并得到 "D")。

如果我两个都想要怎么办?

您只能从 C 构造函数中删除 init() 才能不打印 "C"。 要同时打印 "D",请在 D() 构造函数中添加 init()

如果在某些情况下你想要打印 "C" 或 "D" 而在某些情况下不要这样做

struct C {
   C() { };
   void init() { cout << "C" << endl; };
};

struct D : public C {
   D() : C() 
   { 
     if(some condition)
       C::init();

     if(some condition)
       init();
   };

   void init() { cout << "D" << endl; }
};

D();

why I get "C" printed?

C::init() 未声明为 virtual,因此 D 无法覆盖它。但是,即使 C::init() 被声明为 virtual,当 init() 在 C的构造函数。

C++ 在派生 classes 之前构造基 classes(并在基 classes 之前析构派生类)。所以 C 的构造函数在构造 D 之前运行(并且 C 的析构函数在 D 被破坏之后运行)。 constructed/destructed 对象的 VMT 根本不指向 D 的方法 table 当 C 被 constructed/destructed 时,它指向 C 的方法 table 而不是。

How can change this behaviour (and get "D").

不能从基classconstructor/destructor内部调用派生虚方法。在这些阶段,VMT 不包含指向派生的 class 方法 table 的指针。

这里有一个非常基本的问题:您想在一个尚不存在的对象上调用派生 class 的成员函数。

请记住,对象是通过首先构造基础子对象然后构造派生对象来构造的。因此,即使您设法应用“聪明”的技巧来实际调用派生的 class' init 函数,一旦该函数尝试访问派生对象的任何数据成员,它会造成任意伤害。另一方面,只要您不依赖于构造函数尚未建立的任何不变量,就可以只访问基础对象。因此,如果您不需要访问派生对象的数据,您可以创建 init 函数 static 并将其传递给基 class 对象的引用。

也许这接近您的目标。

#include <iostream>

struct Base
{
  Base(void (*fp)(Base&) = Base::init) { fp(*this); }
  static void init(Base&) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

struct Derived : Base
{
  Derived() : Base(Derived::init) { }
  static void init(Base&) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

int
main()
{
  Base b {};
  std::cout << std::endl;
  Derived d {};
}

输出:

static void Base::init(Base&)

static void Derived::init(Base&)

这里,基础 class 构造函数接受一个指向初始化函数的函数指针,该函数引用 Base 对象。该函数默认为 Base::init 但派生的 classes 可以替换它。但是请注意,在此设计中,Base class 构造函数可能无法安全地假设 Base::init 的任何副作用实际发生了。不过,它作为一种扩展机制很好(如果 Base::init 什么都不做或者是一次性的)。

但我怀疑你是否需要使用这种机器。如果你想做的——这应该是正常情况——首先初始化基对象,然后初始化派生对象,如果你简单地从各自的构造函数中调用函数,C++ 已经默认做正确的事情。

struct Base
{
  Base() { this->init(); }
  void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

struct Derived : Base
{
  Derived() { this->init(); }
  void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

// main() as above ...

输出:

void Base::init()

void Base::init()
void Derived::init()

如果我们只想调用最派生的 class' init 函数,我们可以简单地告诉基础 class 不要 运行 它自己的。

struct Base
{
  Base(const bool initialize = true) { if (initialize) this->init(); }
  void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

struct Derived : Base
{
  Derived() : Base(false) { this->init(); }
  void init() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

// main() as above ...

输出:

void Base::init()

void Derived::init()