【问题标题】:Initialisation list for an member object without default constructor没有默认构造函数的成员对象的初始化列表
【发布时间】:2014-03-25 18:39:41
【问题描述】:

我读到了在类中初始化的引用和对象。但是,除了提示之外,我找不到对以下问题的明确陈述:

如果没有默认构造函数(没有Object(){}),我可以在初始化列表中初始化成员对象obj1吗?

class Sample
{
private:
      Object1 obj1(arguments);
public:
     Sample(Object1 o1)  : obj1( o1(arguments) )
     { }
};

问题出现了,因为如果有与此How can I initialize C++ object member variables in the constructor? 相关的问题。代码也是从那里获取的。感谢您的努力。

丹尼尔

编辑:

由于答案表明它有效,因此测试返回了一个错误(这正是我问这个问题的原因):

../src/Timestep.h:45:12: error: field ‘myFEMSolver’ has incomplete type FEMSolver myFEMSolver;

代码:

class Timestep {
public:
  Timestep();
private:
  FEMSolver myFEMSolver;
}

Timestep::Timestep() : myFEMSolver(*this)
  { //do some stuff
}

FEMSolver::FEMSolver(const Timestep& theTimestep) : myTimestep(theTimestep)
  { //do some stuff
}

main(){
  Timestep myTimestep();
}

【问题讨论】:

  • 该代码不会产生该错误。即,当 FEMSolver 类本身被声明但在 TimeStep 被定义时未定义时,您会收到该错误。 FEMSolver 必须首先完全定义。这与初始化列表无关。 meta.stackexchange.com/questions/66377/what-is-the-xy-problem
  • 其实不是XY问题。最初的问题对于意识到我打算做的事情确实有效很重要。但你对宣言的看法是对的。我切换了标题,现在它可以工作了。非常感谢。
  • X 是“产生这种不完整类型错误的原因”。 Y 是“如果没有默认构造函数,我可以在初始化列表中初始化一个成员对象吗”这是非常一个 X/Y 问题。

标签: c++ class initialization


【解决方案1】:

如果没有默认构造函数(没有Object(){}),我可以在初始化列表中初始化成员对象obj1吗?

是的。事实上,您必须这样做。

class Sample
{
    private:

        Object1 obj1;

    public:

        Sample() : obj1( /* ctor args */ )
        {
        }
};

如果您将另一个 Object1 传递给您的 Sample 构造函数,并将其传递给 Object1 的复制构造函数,那么您可能希望通过引用传递它:

Sample(const Object1& o) : obj1(o)
{
}

如果Object1 没有无参数构造函数,并且您没有在初始化列表中初始化obj1,则会出现编译错误。

【讨论】:

    【解决方案2】:

    如下更改你的类声明:

    class Sample
    {
    private:
          Object1 obj1;
    public:
         Sample(const Object1& o1)  : obj1(o1)
         { }
    };
    

    要调用Sample 构造函数,请使用:

    Sample s(Object1(arguments));
    

    这要求您的Object1 是一个所谓的nice 类(提供复制构造函数和赋值运算符)。
    对于任何其他情况,您需要使用 Object1 的引用或指针作为 Sample 类中的成员。

    另一种(更简单)的方法是传递参数:

    class Sample
    {
    private:
          Object1 obj1;
    public:
         Sample(Args& arguments)  : obj1(arguments)
         { }
    };
    

    【讨论】:

      【解决方案3】:

      看来您要么想在列表初始化器中提供现有的Object1,要么在嵌套的列表初始化器中创建Object1,除非Object1 的构造函数是显式的。所以假设我们有

      struct Object1
      {
          Object1(int, int) { };
      };
      

      那么在Sample 中通过复制、移动或它自己的参数来初始化Object1 的最通用方法是:

      class Sample
      {
      private:
          Object1 obj1;
      public:
          Sample(Object1&& o1) : obj1(std::move(o1)) { }
          Sample(const Object1& o1) : obj1(o1) { }
      
          template<
              typename... A,
              typename = typename std::enable_if<std::is_constructible<Object1, A...>{}>
          >
          Sample(A&&... a) : obj1(std::forward<A>(a)...) { }
      };
      

      这允许我们使用多种Sample 构造:

      int main ()
      {
          Object1 o1{1, 2};
          const Object1& o2{o1};
          Sample s1{Object1{1, 2}};
          Sample s2{{1, 2}};
          Sample s3{1, 2};
          Sample s4{o1};
          Sample s5{o2};
      }
      

      如果Object1的构造函数是explicit,那么s2是不允许的。根据您想要支持的内容,您当然可以通过多种方式简化Sample。

      【讨论】:

        猜你喜欢
        • 2021-05-28
        • 2012-01-28
        • 1970-01-01
        • 2015-10-07
        • 1970-01-01
        • 1970-01-01
        • 2011-06-12
        • 1970-01-01
        相关资源
        最近更新 更多