在这种情况下我们可以避免使用默认构造函数吗?

时间:2013-11-25 17:45:27

标签: c++

观察: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提供默认构造函数。但是,我想知道是否有更好的解决方案来解决上述问题?

5 个答案:

答案 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或任何其他成员函数时,您所使用的对象的语言保证您存在。由于aClassMain的成员,因此必须在ClassMain中的任何函数调用之前构建它。

你在这里唯一的机会是重构代码。