观察:ClassMain的构造函数需要先调用Init
才能构造成员变量a
。由于ClassA
没有默认构造函数,因此代码无法编译。
ClassA
{
public:
// This class has no default constructor
ClassA(...){}
};
class ClassMain
{
public:
ClassMain(...) {
Init(...);
a = ClassA(...); // error: ClassA has no default constructor
// a has to been constructed after the Init is called!
}
ClassMain(...) {
Init(...);
call other functions
a = ClassA(...);
}
private:
// initialize environment
void Init(...) {}
private:
ClassA a;
};
问题>简单的解决方案是为ClassA
提供默认构造函数。但是,我想知道是否有更好的解决方案来解决上述问题?
答案 0 :(得分:2)
更好的解决方案是根本不需要Init
功能。你正在尝试重新构建构造函数,并在此过程中破坏它们的设计。
如果Init
对构造函数做了太多工作,那么在外面做,并将结果资源作为构造函数参数传递给ClassMain
;注意你是如何在构造函数范围内完成所有工作的,因此在正确初始化时没有任何明显的信息。
当然,如果您在初始化a
之前必须执行大量工作,并且无法从外部传递ClassA&
并从中进行初始化,那么您只需要让a
成为间接成员。
您可以使用一个讨厌的解决方法:让Init
实际上是基础构造函数...
答案 1 :(得分:2)
显而易见的解决方案是从早期成员或基类的初始化列表中调用Init()
。构建此子对象后,其结果可以传递给其他子对象的构造函数。例如,在定义流类时,我通常私有地从包含流缓冲区的虚拟基础继承:
struct somebuf_base {
somebuf sbuf;
// ...
};
class somestream
: private virtual somebuf_base
, public std::ostream
{
public:
somestream(someargs)
: somebuf_base(someargs)
, std::ostream(&this->sbuf) {
}
// ...
};
由于基类是按照它们出现的顺序构建的,但是在非虚拟基数之前是虚拟基础,因此首先构造包含sbuf
成员的基类。它的构造函数替换了Init()
函数。
从2011版开始使用C ++时,您也可以使用转发构造函数在多个构造函数之间共享逻辑。
答案 2 :(得分:0)
指向ClassA更容易;因此,您可以随时实例化它(在init()之后) 如果您使用了指针,请不要忘记实现虚拟析构函数并释放为ClassA * a分配的内存
答案 3 :(得分:0)
如果你绝对必须在构造函数的开头调用某个函数,并且不能将该设置放入某个基类或早期构造的成员中,你可以使用这个丑陋的技巧:
ClassMain::ClassMain(int main_param)
: a( (Init(init_arg), class_a_arg1), class_a_arg2 )
{
}
答案 4 :(得分:0)
在这种情况下:不,我们无法避免。
原因是当调用Init
或任何其他成员函数时,您所使用的对象的语言保证您存在。由于a
是ClassMain
的成员,因此必须在ClassMain
中的任何函数调用之前构建它。
你在这里唯一的机会是重构代码。