If your design wants virtual dispatch into a derived class from a base class constructor or destructor for functions like f and g, you need other techniques, such as a post-constructor -- a separate member function the caller must invoke to complete initialization, which can safely call f and g because in member functions virtual calls behave normally. Some techniques for this are shown in the References. Here's a non-exhaustive list of options:
如果您想要为f和g之类的函数设计从基类构造函数或析构函数到派生类的虚分发,则需要其他技术,例如后构造函数-调用者必须调用一个单独的成员函数才能完成初始化,可以安全地调用f和g,因为在成员函数中,虚拟调用的行为正常。参考文献中显示了一些用于此目的的技术。以下是非详尽的选项列表:
Here is an example of the last option:
这是最后一个选项的示例:
class B {
public:
B()
{
/* ... */
f(); // BAD: C.82: Don't call virtual functions in constructors and destructors
/* ... */
}
virtual void f() = 0;
};
class B {
protected:
class Token {};
public:
// constructor needs to be public so that make_shared can access it.
// protected access level is gained by requiring a Token.
explicit B(Token) { /* ... */ } // create an imperfectly initialized object
virtual void f() = 0;
template<class T>
static shared_ptr<T> create() // interface for creating shared objects
{
auto p = make_shared<T>(typename T::Token{});
p->post_initialize();
return p;
}
protected:
virtual void post_initialize() // called right after construction
{ /* ... */ f(); /* ... */ } // GOOD: virtual dispatch is safe
}
};
class D : public B { // some derived class
protected:
class Token {};
public:
// constructor needs to be public so that make_shared can access it.
// protected access level is gained by requiring a Token.
explicit D(Token) : B{ B::Token{} } {}
void f() override { /* ... */ };
protected:
template<class T>
friend shared_ptr<T> B::create();
};
shared_ptr<D> p = D::create<D>(); // creating a D object
This design requires the following discipline:
此设计需要遵循以下原则:
If the requirements above are met, the design guarantees that post_initialize has been called for any fully constructed B-derived object. post_initialize doesn't need to be virtual; it can, however, invoke virtual functions freely.
如果满足上述要求,则设计将确保已为所有完全构造的B派生对象调用post_initialize。post_initialize不需要是虚拟的;但是,它可以自由调用虚拟函数。
In summary, no post-construction technique is perfect. The worst techniques dodge the whole issue by simply asking the caller to invoke the post-constructor manually. Even the best require a different syntax for constructing objects (easy to check at compile time) and/or cooperation from derived class authors (impossible to check at compile time).
总之,没有任何后建技术是完美的。最糟糕的技术是通过简单地要求调用者手动调用后构造函数来规避整个问题。即使是最好的技术,也需要使用不同的语法来构造对象(在编译时易于检查)和/或派生类作者的合作(在编译时无法检查)。
原文链接https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#discussion-use-a-factory-function-if-you-need-virtual-behavior-during-initialization