如何正确初始化不可默认构造的类成员

How to properly initialize non-default-constructible class member?

本文关键字:成员 默认 何正确 初始化      更新时间:2023-10-16

假设我定义了一个类Foo,它没有实现默认构造函数。此外,我有一个类Bar,它"拥有"Foo的实例:

class Foo() {
  private:
    int m_member;
  public:
    Foo( int value ) : m_member(value) { }
};
class Bar() {
  private:
    Foo m_foo;
  public:
    Bar( /* ... */ ) {
      int something;
      /* lots of code to determine 'something' */
      /* should initialize m_foo to 'Foo(something)' here */
    }
};

所示的代码将无法运行,因为Bar正在尝试调用Foo的默认构造函数。

现在我要做的是让Bar的构造函数首先确定something,然后将结果传递给Foo的构造函数。

解决这个问题的一种方法是让Bar只拥有一个指向Foo的引用/指针,并在m_something确定后初始化它。然而,我想避免这种情况,以明确m_foo的生命周期完全依赖于拥有类的生命周期。

另一种方法是在Foo中实现默认构造函数并稍后设置值,这也是我想避免的,因为Foo的任何实例(在任何时候)都应该有其成员的有效值。

实现这一点的正确方法是什么?我在这里被引用/指针卡住了吗?

最好的方法是创建辅助函数,它将计算一些东西,然后在构造器初始化列表中初始化m_foo

class Bar {
  private:
    Foo m_foo;
  public:
    Bar( /* ... */ ) : m_foo(calculate_something()) {
    }
private:
    static int calculate_something()
    {
       int something = 0;
       // lot of code to calculate something
       return something;
    }
};

这个复杂的初始化代码实际上属于 Bar吗?最好考虑使用一个单独的类来完成初始化。就像

class Bar {
  public:
    Bar(int param, Foo foo): m_foo(foo) {
        // do just some very simple calculations, or use only constructor initialization list
    }
  ...
}
class BarBuilder {
  public:
    BarBuilder(/*...*/) {
        // do all calculations, boiling down to a few parameters for Bar and Foo
       Foo foo(fooParameter);
       m_result = new Bar(barParameter, foo); // give Foo here explicitly
    }
    Bar getResult() { return *m_result; }
  private:
    Bar* m_result; // or better use unique_ptr  
}

这也为一个完整的Builder模式打开了道路,在某些情况下,例如,您并不总是需要所有复杂的计算。

这假定所有的类都是可复制构造的,但是您可以或多或少地修改它以支持您需要的内容。