避免调用成员变量的构造函数
Avoid calling constructor of member variable
我有以下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)
-方式工作。我不想用new
动态地分配它。
如何使用本QA中描述的技术?
防止在类
中调用数组的默认构造函数std::aligned_storage<sizeof(T[n]), alignof(T)>::type
或者,您也可以考虑使用union
。当然,联合只能用第一个命名成员的构造函数初始化。
union
{
uint8_t _nothing = 0;
C c;
};
根据QA中提到的标准,c
将被零初始化,并且不会调用其构造函数
你不能。
当进入构造代码块时,所有成员变量都是完全构造的。这意味着必须调用这些构造函数。
但是你可以绕过这个限制。
// 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))
{
}
我看不到一个好的方法来实现你想要的。这必须是一个解决方案:
// 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跟踪是否正确初始化,如果没有初始化使用,则返回适当的错误。
使用逗号表达式:
A::A()
: m_B(1)
, m_c((m_B.doSomething(), m_B.doMore(), m_B.getSomeValue()))
{
}
显然,正如其他人解释的那样,m_B
最好在m_C
之前声明,否则m_B.doSomething()
会调用未定义的行为。
对我来说,指针听起来是唯一干净的解决方案。我看到的唯一其他解决方案是为C提供一个默认构造函数,它不做任何事情,并且在C中有一个初始化方法,稍后您可以自己调用。
m_C。initialize (m_B.getSomeValue());
最简单的是存储指向B
和C
的指针。这些可以初始化为0,省略任何构造。注意不要在A
的析构函数中解引用空指针并删除它(或者使用std::unique_ptr
/boost::scoped_ptr
)。
但是为什么不首先初始化m_B
(通过适当的构造函数调用,而不是在A::A()
中),然后使用初始化的B
实例初始化m_C
?这需要稍微重写一下,但我打赌代码清理是值得的。
如果出于代码混乱/异常安全的原因,您不想使用new
动态分配它,您可以使用std::unique_ptr
或std::auto_ptr
来解决这个问题。
避免new
的解决方案是编辑C
,使其具有两步初始化过程。然后构造函数将构造一个"僵尸"对象,您必须在该m_C
实例上调用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))
{
}
请注意,这意味着你不能允许m_B
依赖于A
或C
的实例,而在这个答案的顶部的解决方案可能允许你传递A
或m_C
到m_B
的方法。
我们有了构建模块:
#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()
- 没有用于初始化C++中的变量模板的匹配构造函数
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 在类构造函数中使用结构变量
- 在 c++ 中将变量作为结构构造函数中的引用传递
- 在类构造函数中定义结构变量的参数
- 修改程序的入口点时未调用全局变量的构造函数
- C++:将向量传递到构造函数以创建成员变量的最佳方法?
- 为什么从另一个构造函数内部调用C++构造函数不修改类变量?
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 构造函数干扰成员变量指定的初始值设定项?
- 类中的数组变量C++导致"was not declared in this scope"实现文件的一个函数中错误,但在构造函数中不会导致错误
- 通过 C++ 中的重载构造函数初始化未知类型的变量
- 如何在初始化列表中的构造函数之后初始化变量/对象?
- 我可以使用在类构造函数中初始化的流类型的成员变量吗?
- 为什么 std::move 不将默认移动构造函数中的源变量更改为默认值?
- C++为具有引用成员变量的类创建复制构造函数
- 如何在全局变量的构造函数之前运行函数
- C++/Win32 构造函数不使用从对话框获取的字符串初始化变量
- 将内置类型变量传递给只有一个类类型参数的"+"运算符函数时自动类型转换的构造函数
- std::线程构造函数(变量的数量)