C++ 避免调用成员变量的构造函数
我有以下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
// 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
的方法中。最简单的方法是存储指向AB
和AC
的指针。这些可以初始化为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()