13

我有以下 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());
}

我现在想避免class A调用. _ C m_C因为在最后一行A::A(),我无论如何都要初始化m_C自己,因为我需要先做好准备m_B。我可以为class B. 但这不是想法。

我已经尝试添加m_C(NULL)A::A(). 有时它有效,有时它说没有构造函数NULL作为参数。

那么我怎么能m_C保持未初始化呢?我知道使用指针,m_C(NULL)-way 有效。而且我不想使用动态分配它new

任何想法都值得赞赏。

4

9 回答 9

7

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

防止调用类内数组的默认构造函数

std::aligned_storage<sizeof(T[n]), alignof(T)>::type

或者,您也可以考虑使用 of union。AFAIK,联合将仅使用第一个命名成员的构造函数进行初始化。

例如,

union
{
   uint8_t _nothing = 0; 
   C c;
};

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

于 2014-04-28T06:37:20.057 回答
3

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

// 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 的想法更新了代码。

于 2011-10-20T09:40:02.507 回答
3

你不能。

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

但是您可以解决此限制。

// 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))
{
}
于 2011-10-20T09:42:11.697 回答
1

你所问的都是被禁止的——而且是正确的。这确保了每个成员都被正确初始化。不要试图解决它 - 尝试构建他们使用的类。

主意:

  • C有一个什么都不做的构造函数
  • C 有一个使类可用的初始化方法
  • C 跟踪它是否已正确初始化,如果在未初始化的情况下使用,则返回适当的错误。
于 2011-10-20T09:40:54.517 回答
0

如果出于代码混乱/异常安全原因不想动态分配它new,可以使用std::unique_ptrorstd::auto_ptr来解决这个问题。

避免的解决方案new是编辑C具有两步初始化过程。然后构造函数将构造一个“僵尸”对象,您必须Initialize在该实例上调用一个方法m_C才能完成初始化。这类似于您发现的现有案例,您可以将其传递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))
{
}

请注意,这意味着您不能允许m_B依赖Aor的实例C,而此答案顶部的解决方案可能允许您传递Aorm_Cm_B方法。

于 2011-10-20T09:39:55.250 回答
0

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

但是为什么不m_B先初始化(通过适当的构造函数调用,而不是 in A::A(),然后使用初始化B的实例来初始化m_C?这将需要一个小的重写,但我敢打赌,代码清理是值得的。

于 2011-10-20T09:40:00.963 回答
0

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

m_C.Initialise(m_B.getSomeValue());

于 2011-10-20T09:42:40.290 回答
0

只需使用逗号表达式:

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

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

于 2011-10-20T10:14:31.853 回答
0

这里我们有构建块:

#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 创建一种新的构造,请考虑创建一个派生类:

class B1 : public B
{
public:
  B1() : B(1)
  {
    doSomething();
    doMore();
  }
};

现在使用从 B 派生的类 B1:

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()
于 2014-06-24T20:15:33.990 回答