简体   繁体   English

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

[英]Calling a member function of a derived class from the base class constructor

Suppose I have 假设我有

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

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

D();

why I get "C" printed? 为什么我会打印“ C”? How can change this behaviour (and get "D"). 如何改变这种行为(并得到“ D”)。

How if I want both? 如果我两个都想怎么办?

why I get "C" printed? 为什么我会打印“ C”?

C::init() is not declared as virtual , so D cannot override it. C::init()未声明为virtual ,因此D无法覆盖它。 But even if C::init() were declared as virtual , D::init() would still not be called when init () is called inside of C 's constructor. 但是,即使将C::init()声明为virtual ,当在C的构造函数内部调用init ()时, 不会调用D::init()

C++ constructs base classes before derived classes (and destructs derived clases before base classes). C ++在派生类之前构造基类(并在基类之前构造派生类)。 So C 's constructor runs before D is constructed (and C 's destructor runs after D is destructed). 因此, C的构造函数在构造D之前运行(而C的析构函数在D C之后运行)。 The VMT of the object being constructed/destructed simply does not point at D 's method table when C is being constructed/destructed, it points at C 's method table instead. 在构造/销毁C ,正在构造/销毁的对象的VMT根本不指向D的方法表,而是指向C的方法表。

How can change this behaviour (and get "D"). 如何改变这种行为(并得到“ D”)。

You cannot call a derived virtual method from inside of a base class constructor/destructor. 不能从基类构造函数/析构函数的内部调用派生的虚拟方法。 The VMT does not contain a pointer to the derived class method table at those stages. 在这些阶段,VMT不包含指向派生类方法表的指针。

You have a quite fundamental problem here: You want to call a member function of a derived class on an object that does not exist yet. 您这里有一个非常基本的问题:您想在尚不存在的对象上调用派生类的成员函数。

Remember that objects are constructed by first constructing the base sub-object and then the derived object. 请记住,首先通过构造基础子对象,然后构造派生对象来构造对象。 So even if you'd manage to apply a “clever” trick to actually invoke the derived class' init function, as soon as that function would try to access any data member of the derived object, it would cause arbitrary damage. 因此,即使您设法应用“巧妙”的技巧来实际调用派生类的init函数,该函数一旦尝试访问派生对象的任何数据成员,也将造成任意损坏。 On the other hand, it is fine to access only the base object, as long as you don't rely on any invariant that the constructor has not established yet. 另一方面,只要您不依赖于构造函数尚未建立的任何不变式,最好只访问基础对象。 Therefore, if you don't need access to the derived object's data, you can make the init function static and pass it a reference to the base class object. 因此,如果您不需要访问派生对象的数据,则可以将init函数设为static ,并将其传递给基类对象的引用。

Maybe this is coming close to what you are trying to do. 也许这接近您要尝试做的事情。

#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 {};
}

Output: 输出:

static void Base::init(Base&)

static void Derived::init(Base&)

Here, the base class constructor takes a function pointer to an initializer function that takes a reference to a Base object. 在这里,基类构造函数使用指向初始化函数的函数指针,该初始化函数引用对Base对象的引用。 The function defaults to Base::init but derived classes can replace it. 该函数默认为Base::init但是派生类可以替换它。 Be aware, however, that in this design, the Base class constructor may not safely assume that any side effect of Base::init actually took place. 但是请注意,在此设计中, Base类构造函数可能无法安全地假定Base::init确实发生了任何副作用。 It is fine as an extension mechanism (if Base::init does nothing or is disposable), though. 不过,它可以作为扩展机制使用(如果Base::init不执行任何操作或可抛弃)。

But I doubt that you need to use this kind of machinery. 但是我怀疑您是否需要使用这种机器。 If all you want to do – and this should be the normal case – is to first initialize the base object and then the derived object, C++ already will do the right thing by default if you simply call the functions from the respective constructors. 如果您想做的所有事情(这应该是正常情况)是首先初始化基础对象,然后再初始化派生对象,那么如果您仅从各个构造函数中调用函数,则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 ...

Output: 输出:

void Base::init()

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

And if we only want to call the most derived class' init function, we can simply tell the base class not to run its own. 而且,如果我们只想调用派生程度最高的类的init函数,则可以简单地告诉基类不要运行自己的基类。

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 ...

Output: 输出:

void Base::init()

void Derived::init()

You only can remove init() from C constructor to not print "C". 您只能从C构造函数中删除init()而不打印“ C”。 To also print "D" add init() in D() constructor. 要同时打印“ D”,请在D()构造函数中添加init()

If for some cases you want print "C" or "D" and in some don't do something like this 如果在某些情况下您想要打印“ 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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM