【问题标题】:Construct an object, pass it to the base class constructor keeping control of its lifetime构造一个对象,将其传递给基类构造函数,以控制其生命周期
【发布时间】:2015-05-28 23:58:59
【问题描述】:

我需要从 Base 类派生一个 Child 类,满足以下要求:

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

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

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

A.让 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) ...

这会将 ref 传递给临时类 Base Constructor(它甚至会编译吗?),然后单独初始化 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 作为 Child 类成员:

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 也不会被调用。我能做什么?

【问题讨论】:

    标签: c++ constructor


    【解决方案1】:

    使用Base-from-Member Idiom - 创建另一个 类,该类拥有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 - 因为它肯定会在Base 之前构建。

    如果你觉得因为某种原因太冗长,你也可以使用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)
        { }
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-04
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 2013-05-11
      相关资源
      最近更新 更多