首页 文章详情

C++核心准则​讨论:如果在初始化期间需要“虚行为”,请使用工厂函数

面向对象思考 | 455 2020-12-11 00:27 0 0 0
UniSMS (合一短信)

Discussion: Use a factory function if you need "virtual behavior" during initialization

讨论:如果在初始化期间需要“虚行为”,请使用工厂函数


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,因为在成员函数中,虚拟调用的行为正常。参考文献中显示了一些用于此目的的技术。以下是非详尽的选项列表:

  • Pass the buck: Just document that user code must call the post-initialization function right after constructing an object.

    转移责任:只需说明用户代码在构造对象后必须立即调用初始化后的函数。

  • Post-initialize lazily: Do it during the first call of a member function. A Boolean flag in the base class tells whether or not post-construction has taken place yet.

    延迟后初始化:在成员函数的第一次调用期间执行此操作。基类中的布尔值标志指示是否进行了后期构造。

  • Use virtual base class semantics: Language rules dictate that the constructor most-derived class decides which base constructor will be invoked; you can use that to your advantage. (See [Taligent94].)

    使用虚拟基类语义:语言规则规定,最(后,译者注)派生类的构造函数决定将调用哪个基类构造函数;您可以利用它来发挥自己的优势。(请参阅[Taligent94]。)

  • Use a factory function: This way, you can easily force a mandatory invocation of a post-constructor function.

    使用工厂函数:这样,您可以轻松强制强制调用后构造函数。

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
static shared_ptr create() // interface for creating shared objects
{
auto p = make_shared(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
friend shared_ptr B::create();
};

shared_ptr p = D::create(); // creating a D object

This design requires the following discipline:

此设计需要遵循以下原则:

  • Derived classes such as D must not expose a publicly callable constructor. Otherwise, D's users could create D objects that don't invoke post_initialize.

    诸如D之类的派生类不得公开可调用的构造函数。否则,D的用户可以创建不调用post_initialize的D对象。

  • Allocation is limited to operator new. B can, however, override new (see Items 45 and 46 in SuttAlex05).

    分配仅限于new运算符。但是,B可以覆盖new(请参见SuttAlex05中的项目45和46)。

  • D must define a constructor with the same parameters that B selected. Defining several overloads of create can assuage this problem, however; and the overloads can even be templated on the argument types.

    D必须使用与B选择的参数相同的参数定义一个构造函数。但是,定义几个create的重载可以缓解这个问题。甚至可以定义有关参数类型的模板形式重载。

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


新书介绍

《实战Python设计模式》是作者最近出版的新书,拜托多多关注!

本书利用Python 的标准GUI 工具包tkinter,通过可执行的示例对23 个设计模式逐个进行说明。这样一方面可以使读者了解真实的软件开发工作中每个设计模式的运用场景和想要解决的问题;另一方面通过对这些问题的解决过程进行说明,让读者明白在编写代码时如何判断使用设计模式的利弊,并合理运用设计模式。

对设计模式感兴趣而且希望随学随用的读者通过本书可以快速跨越从理解到运用的门槛;希望学习Python GUI 编程的读者可以将本书中的示例作为设计和开发的参考;使用Python 语言进行图像分析、数据处理工作的读者可以直接以本书中的示例为基础,迅速构建自己的系统架构。




觉得本文有帮助?请分享给更多人。

关注微信公众号【面向对象思考】轻松学习每一天!

面向对象开发,面向对象思考!



good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter