假设我有一个封装一个(或多个)成员的类,它必须以某种方式进行初始化,没有合理的方法来使用没有它的类(所以我不想让它成为可选的) 。 在其构造函数中运行初始化是否更好呢:
class MyClass
{
MyClass()
{
if(!obj.initialize()
throw ...;
}
private:
MyObject obj;
}
或者您会建议以下设计:
class MyClass
{
MyClass()
{
}
bool initialize()
{
return obj.initialize();
}
private:
MyObject obj;
}
第一个看起来很吸引人,因为我可以保证在构造函数运行后满足使用我的类的所有要求,并且我可以通过抛出异常来报告任何错误。
第二个看起来不错,因为它不会使构造函数超出直觉上不属于那里的东西,特别是一旦初始化例程变得复杂并且即创建小部件,打开数据库连接,初始化第三方库等等。我正在使用的遗留代码,ctors充满了参数和初始化的东西,可能在这种类型的膨胀对象构造甚至完成之前运行数千个代码行。试图将这些重构为更干净的东西在某些方面真的很难,因为涉及的依赖性太多了。这就是我提出问题的原因。
我可以看到设计#2的一大缺点是我需要一个客户端必须记住调用的公共初始化例程。但由于这可能并且可能会被遗忘,我必须在所有公共成员中跟踪和检查初始化状态,并以某种方式处理错误(可能是断言会做的)。如果我选择设计#1,这也会使我的课程变得杂乱无章。
那么这里最好的选择是什么?
答案 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
的隐式定义的默认构造函数将完成正确的工作。