选择哪种设计进行复杂对象初始化?

时间:2016-02-11 11:57:00

标签: c++ constructor initialization

假设我有一个封装一个(或多个)成员的类,它必须以某种方式进行初始化,没有合理的方法来使用没有它的类(所以我不想让它成为可选的) 。 在其构造函数中运行初始化是否更好呢:

class MyClass
{
    MyClass()
    {
        if(!obj.initialize()
            throw ...;
    }

private:
    MyObject obj;
}

或者您会建议以下设计:

class MyClass
{
    MyClass()
    {
    }

    bool initialize()
    {
        return obj.initialize();
    }

private:
    MyObject obj;
}

第一个看起来很吸引人,因为我可以保证在构造函数运行后满足使用我的类的所有要求,并且我可以通过抛出异常来报告任何错误。

第二个看起来不错,因为它不会使构造函数超出直觉上不属于那里的东西,特别是一旦初始化例程变得复杂并且即创建小部件,打开数据库连接,初始化第三方库等等。我正在使用的遗留代码,ctors充满了参数和初始化的东西,可能在这种类型的膨胀对象构造甚至完成之前运行数千个代码行。试图将这些重构为更干净的东西在某些方面真的很难,因为涉及的依赖性太多了。这就是我提出问题的原因。

我可以看到设计#2的一大缺点是我需要一个客户端必须记住调用的公共初始化例程。但由于这可能并且可能会被遗忘,我必须在所有公共成员中跟踪和检查初始化状态,并以某种方式处理错误(可能是断言会做的)。如果我选择设计#1,这也会使我的课程变得杂乱无章。

那么这里最好的选择是什么?

2 个答案:

答案 0 :(得分:4)

  

“第一个看起来很吸引人,因为我可以保证在构造函数运行后满足使用我的类的所有要求......”

必须,否则设计不好。

当构造函数完成时,对象 必须可以在没有任何未定义的行为且根据其接口规范的情况下使用。

意味着对象需要配置以用于特定目的。

我想从配置中分离初始化

例如,请查看std::fstream。您可以在不打开任何文件的情况下创建完全初始化的 fstream对象

std::fstream fs; // initialized but not configured

它没有表现出未定义的行为,并将根据其接口规范进行操作。

因此,您可以将其界面配置用于特定目的 - 例如,读取特定文件:

fs.open("myfile.txt", std::ios::in); // configured

默认构造函数应该执行绝对最小值以将对象放入工作订单,而不必< em>将配置为给定任务。

话虽如此,没有理由不让其他构造函数更容易创建配置的对象:

std::fstream fs("myfile.txt", std::ios::in); // initialized & configured

答案 1 :(得分:3)

将代码从MyObject::initialize移至MyObject的构造函数(如果需要,还可以移动throw)。现在,MyClass的隐式定义的默认构造函数将完成正确的工作。