C++ 避免调用成员变量的构造函数

C++ 避免调用成员变量的构造函数,c++,initializer-list,C++,Initializer List,我有以下C++类: // Header-File class A { public: A(); private: B m_B; C m_C; }; // cpp-File A::A() : m_B(1) { m_B.doSomething(); m_B.doMore(); m_C = C(m_B.getSomeValue()); } 我现在想避免A类调用任何构造函数cmuc。因为在A::A()中的最后一行,我将自己初始化muc

我有以下C++类:

// Header-File
class A
{
    public:
    A();

    private:
    B m_B;
    C m_C;
};

// cpp-File
A::A()
: m_B(1)
{
    m_B.doSomething();
    m_B.doMore();
    m_C = C(m_B.getSomeValue());
}
我现在想避免
A类
调用任何构造函数
cmuc
。因为在
A::A()
中的最后一行,我将自己初始化
muc
,因为我需要先准备
mub
。我可以为
类B
提供一个空的默认构造函数。但这不是我的想法

我已经尝试将
m_C(NULL)
添加到
A::A()
的init列表中。有时它有效,有时它说没有构造函数将
NULL
作为参数

那么,我怎样才能让
muc
保持未初始化状态呢?我知道,对于指针,
mc(NULL)
-方法是有效的。我不想使用
new
动态分配它


任何想法都值得赞赏。

如果出于代码混乱/异常安全原因,您不想使用
new
动态分配它,您可以使用
std::unique\u ptr
std::auto\u ptr
来解决此问题

避免
new
的一个解决方案是编辑
C
以进行两步初始化过程。然后构造函数将构造一个“僵尸”对象,您必须在该
muc
实例上调用
Initialize
方法来完成初始化。这与您发现的现有情况类似,您可以将
NULL
传递给构造函数,然后返回初始化对象

编辑:

我早就想到了这一点(尽管它看起来很像其他人的解决方案)。但我必须得到一些确认,在我添加这个解决方案之前,这不会中断。C++是非常棘手的,而且我不经常使用它:

这比我的其他建议更简洁,并且不需要您去处理任何实现,除了
A
的实现

只需使用静态方法作为初始化的中间人:

class A
{
public:
    A();

private:
    static int InitFromB(B& b)
    {
        b.doSomething();
        b.doMore();
        return b.getSomeValue();
    }

    // m_B must be initialized before m_C
    B m_B;
    C m_C;
};

A::A()
    : m_B(1)
    , m_C(InitFromB(m_B))
{
}

请注意,这意味着您根本不能允许
mub
依赖
A
C
的实例,而此答案顶部的解决方案可能允许您将
A
muc
传递到
mub
的方法中。

最简单的方法是存储指向A
B
和A
C
的指针。这些可以初始化为0,省略任何构造。小心不要取消引用空指针并在
a
的析构函数中删除它(或使用
std::unique_ptr
/
boost::scoped_ptr

但是为什么不先初始化
mub
(通过正确的构造函数调用,而不是在
a::a()中)
,然后使用已初始化的
B
实例初始化
m_C
?这将需要一个小的重写,但我打赌它值得代码清理。

我看不到一个好方法来实现您想要的。这必须是一个解决方法:

// Header-File
class A
{
    public:
    A();

    private:
    B m_B;
    C m_C;
    static int prepareC(B& b);
};

// cpp-File
A::A()
: m_B(1)
, m_C(prepareC(m_B))
{
}

int A::prepareC(B& b)
{
    b.doSomething();
    b.doMore();
    return b.getSomeValue();
}
请确保
m_B.doSomething()
m_B.doMore()
m_B.getSomeValue()
不要(直接或间接)触摸
m_C


正如@Tobias正确提到的,此解决方案取决于初始化的顺序。您需要确保
m_B
m_C
的定义按此顺序



根据@Loki的想法更新了代码。

您的请求是禁止的,而且是正确的。这确保了每个成员都正确初始化。不要试图解决它,尝试构建您的类,使它们能够使用它

想法:

  • C有一个不做任何事情的构造函数
  • C有一个使类可用的初始化方法
  • C跟踪它是否已正确初始化,如果在没有初始化的情况下使用,则返回相应的错误
    • 你不能

      当输入construcotr代码块时,所有成员变量都是完全构造的。这意味着必须调用这些构造函数

      但是你可以绕过这个限制

      // Header-File
      class A
      {
          struct Initer
          {
               Initer(B& b)
                   : m_b(b)
               {
                   m_b.doSomething();
                   m_b.doMore();
               }
               operator int()  // assuming getSomeValue() returns int.
               {
                   return m_b.getSomeValue();
               }
               B& m_b;
          };
          public:
          A();
      
          private:   // order important.
          B m_B;
          C m_C;
      };
      
      
      // cpp-File
      A::A()
      : m_B(1)
      , m_C(Initer(m_B))
      {
      }
      

      指针对我来说似乎是唯一干净的解决方案。我看到的另一个解决方案是为C提供一个默认构造函数,它什么都不做,并且在C中有一个初始化方法,稍后您可以自己调用它


      m_C.初始化(m_B.getSomeValue());

      只需使用逗号表达式:

      A::A()
        : m_B(1)
        , m_c(m_B.doSomething(), m_B.doMore(), m_B.getSomeValue())
      {
      }
      

      显然,正如其他人所解释的,
      mub
      最好在
      muc
      之前声明,否则
      mub.doSomething()
      调用未定义的行为。

      如何使用本QA中描述的技术


      根据QA中提到的标准,
      c
      将初始化为零,并且不会调用其构造函数。

      这里我们有构建块:

      #include <iostream>
      
      class C
      {
      public:
        C(int i){std::cout << "C::C(" << i << ")" << std::endl;}
      };
      
      class B
      {
      public:
        B(int i){std::cout << "B::B(" << i << ")" << std::endl;}
        void doSomething(){std::cout << "B::doSomething()" << std::endl;}
        void doMore(){std::cout << "B::doMore()" << std::endl;}
        int getSomeValue(){return 42;}
      };
      
      现在使用从B派生的B1类:

      class A
      {
      private:
        B1 _b;
        C _c;
      public:
        A() : _c(_b.getSomeValue()){std::cout << "A::A()" << std::endl;}
      };
      
      输出:

      B::B(1)
      B::doSomething()
      B::doMore()
      C::C(42)
      A::A()
      

      一种邪恶的方法是将“char m_C_data[sizeof(C)]作为成员,然后在初始化时对其进行新的放置。这将避免堆分配,但每次使用时都必须将其强制转换为正确的类型。当然,必须记住类型C的复杂性(是一个POD,有vftable,C类是否有一个默认构造函数,它什么也不做?或者再写一个构造函数,它将NULL作为参数,并使它显式地安全,什么也不做。直到现在,它还没有。尽管我不希望它有一个默认构造函数,但看起来我不得不将其更改为有一个。遗憾的是接受的答案是不正确的。别忘了向下滚动…你必须知道,这一行为取决于m_B和m_C定义的正确顺序。只要m_C在m_B之后定义,它就可以工作。请这样评论。否则我喜欢这个版本。+1:尽管我会做prepareC()静态成员并将b作为参数传递。对我来说似乎是一种可能性。为什么我必须确保
      m_b
      的方法不接触
      m_C
      ?@Atmocreations:因为这将是未定义的行为:
      m_Cclass B1 : public B
      {
      public:
        B1() : B(1)
        {
          doSomething();
          doMore();
        }
      };
      
      class A
      {
      private:
        B1 _b;
        C _c;
      public:
        A() : _c(_b.getSomeValue()){std::cout << "A::A()" << std::endl;}
      };
      
      int main()
      {
        A a;
      }
      
      B::B(1)
      B::doSomething()
      B::doMore()
      C::C(42)
      A::A()