我有以下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
动态分配它。
任何想法都表示赞赏。
答案 0 :(得分:4)
我没有看到良好的方式来实现您想要的效果。这必须是一种解决方法:
// 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的想法更新了代码。
答案 1 :(得分:3)
你不能。
输入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))
{
}
答案 2 :(得分:3)
整蛊,但可以做到。
您需要的是一种向成员变量添加行为的方法。所以变量是初始化的,也可能不是。我们称之为“可能”
如果以通用方式执行此操作,则需要模板类来封装该行为并将其应用于任何类型:
template<class T>
Maybe {
public:
Maybe() : m_has(false) {}
// If we want to start with the value, call the constructor
Maybe(const T& v) : m_has(true) { new (m_value) T(v); }
// If we have some value, make sure to call the destructor
˜Maybe() { if (m_has) reinterpret_cast<T*>(m_value)->˜T(); }
// Add the value latter on
void setValue(const T& v) {
if (m_has) {
reinterpret_cast<T>(*m_value) = v;
} else {
m_has = true;
new (m_value) T(v);
}
}
bool hasValue() const { return m_has; }
const T& value() const { return reinterpret_cast<T&>(*m_value); }
T& value() { return reinterpret_cast<T&>(m_value); }
private:
bool m_has;
// Reserve the memory for the object, but dont initialize it - dont call it T
uint8_t m_value[sizeof(T)];
};
我把代码写到了我的头顶,所以可能会有一些错别字或小细节需要调整。我知道它有效。
现在,只需将您的成员称为Maybe,然后您就不必创建空构造函数。
答案 3 :(得分:3)
如何使用本QA中描述的技术?
Prevent calls to default constructor for an array inside class
std::aligned_storage<sizeof(T[n]), alignof(T)>::type
或者,您也可以考虑使用union
。 AFAIK,unions will be initialized only with first named member's constructor.
例如,
union
{
uint8_t _nothing = 0;
C c;
};
根据QA中提到的标准,c
将被零初始化,并且不会调用其构造函数。
答案 4 :(得分:2)
你要求的是被禁止的 - 而且是正确的。这可确保正确初始化每个成员。不要试图解决它 - 尝试构建他们使用它的类。
点子:
答案 5 :(得分:0)
如果您不希望使用new
动态分配代码混乱/异常安全原因,可以使用std::unique_ptr
或std::auto_ptr
来解决此问题。
避免new
的解决方案是编辑C
以进行两步初始化过程。然后,构造函数将构造一个“zombie”对象,您必须在该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
完全依赖A
或C
的实例,而此答案顶部的解决方案可能允许您将A
或m_C
传递给m_B
的方法。
答案 6 :(得分:0)
最简单的是存储指向B
和C
的指针。这些可以初始化为0,省略任何结构。注意不要取消引用空指针并在A
的析构函数中删除它(或使用std::unique_ptr
/ boost::scoped_ptr
)。
但为什么不首先初始化m_B
(通过正确的构造函数调用,而不是A::A()
,然后使用该初始化的B
实例初始化m_C
?它会调用对于一个小的重写,但我敢打赌它值得代码清理。
答案 7 :(得分:0)
指针听起来像是我唯一干净的解决方案。我看到的唯一其他解决方案是为C设置一个默认构造函数,它不执行任何操作,并且在C中使用初始化方法,稍后会自行调用。
m_C.Initialise(m_B.getSomeValue());
答案 8 :(得分:0)
只需使用逗号表达式:
A::A()
: m_B(1)
, m_c(m_B.doSomething(), m_B.doMore(), m_B.getSomeValue())
{
}
显然,正如其他人所解释的那样,m_B
最好在m_C
之前声明{else} m_B.doSomething()
调用未定义的行为。
答案 9 :(得分: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()