20 October 2022

C++ 核心指南目录

C.50: Use a factory function if you need “virtual behavior” during initialization

理由

如果基类对象的某些状态依赖于继承它的子类对象,我们需要定义虚函数,从而减少误用没有完全构造完成的对象的机会窗口。

注意

工厂函数的返回类型一般默认是unique_ptr,如果某些被使用的地方是共享的,函数调用方可以把unique_ptr移动为shared_ptr。不过,如果编写工厂函数的人明确知道所有的使用方式都是共享使用,那也可以返回shared_ptr并且用在函数中用make_shared保留内存。

例子

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:
    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:
    explicit D(Token) : B{ B::Token{} } {}
    void f() override { cout << "D::f()\n"; }

protected:
    template<class T>
    friend shared_ptr<T> B::create();
};

int main()
{
    shared_ptr<D> p = D::create<D>();  // creating a D object
}
D::f()

make_shared 要求构造函数必须是 public 的。因为需要一个 protectedToken ,构造函数不能公开调用,所以我们要避免没完全构建的对象逃到野外(可被访问)。通过提供工厂函数create()我们可以很方便的在自由存储区中构建对象。

注意

按照惯例,工厂函数在自由存储区中分配对象,而不是在栈或上层对象内分配内存。