前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++核心准则​讨论:如果在初始化期间需要“虚行为”,请使用工厂函数

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

作者头像
面向对象思考
发布2020-12-15 15:03:22
4440
发布2020-12-15 15:03:22
举报

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:

这是最后一个选项的示例:

代码语言:javascript
复制
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:

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

  • 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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 面向对象思考 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Discussion: Use a factory function if you need "virtual behavior" during initialization
  • 讨论:如果在初始化期间需要“虚行为”,请使用工厂函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档