构造一个对象,将其传递给基础 class 构造函数,以控制其生命周期

Construct an object, pass it to the base class constructor keeping control of its lifetime

我需要根据以下要求从 class 基派生一个 class 子代:

  1. Class 基本构造函数接受对 class Foo.
  2. 对象的引用
  3. class Foo 的对象应该与 class Child 的对象具有相同的生命周期。
  4. Foo、Child 和 Base 的构造函数可以抛出,代码应该充分破坏到目前为止创建的内容。
  5. 代码是多线程的,可以同时调用child的多个构造函数
  6. Foo 没有复制构造函数,也没有默认构造函数。

(与How to remind a param passed to the base class constructor?有一些相似之处 然而异常安全几乎没有在上述问题中讨论)

在实现过程中遇到了以下困难:

一个。 class Foo 的对象成为 Child

的成员似乎很自然
class Base {
public:
    Base(Foo& foo);
};

class Child: public Base {
public:
    Child(int par);
private:
    Foo m_foo;
};

但是,现在如何在 Child 构造函数中初始化它?

Child::Child(int par):
Base(Foo(par)),
m_Foo(par) ...

这会将临时引用传递给 class 基本构造函数(它甚至可以编译吗?),然后单独初始化 m_Foo。这很糟糕。

如果不是要求#4 和#5,我可以在调用 Base 时使用静态方法共同构造 Foo,然后将其传递给 m_Foo:

Foo Child::s_Helper(int par) {
    if (!s_FooConstructed) {
        s_Foo = Foo(par);
        s_FooConstructed = true;
    }

    return s_Foo;
}

我愿意使用 unique_ptr 或 shared_ptr 作为子 class 成员:

class Child: public Base {
public:
    Child(int par);
private:
    unique_ptr<Foo> m_Foo;
    bool m_IsFooConstructed;
    unique_ptr<Foo> x_FooHelper(int par);
};

Child::Child(int par):
    Base(x_FooHelper(par)),
    m_Foo(x_FooHelper(par))
{}

unique_ptr <Foo> Child::x_FooHelper(int par)
{
   if(!m_FooConstructed) {
       m_Foo.reset(new Foo(par));      ///< Problem here
       m_FooConstructed = true;
   }
   return m_Foo;
}

然而,此处m_Foo在调用x_FooHelper时并未构建... 与 shared_ptr

相同

我可以创建一个指针:

class Child: public Base {
public:
    Child(int par);
private:
    Foo* m_Foo;
    bool m_IsFooConstructed;
    Foo* x_FooHelper(int par);
};
Child::Child(int par):
    Base(*x_FooHelper(par)),
    m_Foo(x_FooHelper(par))
{}

Foo* Child::x_FooHelper(int par)
{
   if(!m_FooConstructed) {
       m_Foo = new Foo(par);
       m_FooConstructed = true;
   }
   return m_Foo;
}

但是在这里,如果 Base 构造函数抛出,Foo 已经创建,并且不会被销毁,因为它由原始指针保存 (m_Foo) ~Child 也不会被调用。我能做什么?

使用 Base-from-Member Idiom - 创建 另一个 class 拥有 Foo 作为 Child 的第一个基地:

class Base {
public:
    Base(Foo& foo);
};

struct FooOwner {
    FooOwner(int par)
        : m_foo(par)
    { }

    Foo m_foo;
};

class Child
    : private FooOwner  // first, so Foo is initialized
    , public Base       //    before Base
{
public:
    Child(int par)
      : FooOwner(par)
      , Base(FooOwner::m_foo)
    { 
        /* something else */
    }
};

Foo 的生命周期仍然与 Child 相关联,您可以安全地将对它的引用传递给 Base - 因为它肯定会在 [= 之前​​构造16=]。

如果您因为某种原因觉得太冗长,您也可以使用 boost::base_from_member 来实现相同的目的:

class Child
    : private boost::base_from_member<Foo>
    , public Base
{
    typedef boost::base_from_member<Foo> FooOwner;

    Child(int par)
        : FooOwner(par)
        , Base(FooOwner::member)
    { }
};