我一直在做一些阅读,一般意见似乎是尽可能避免两阶段初始化。我同意它所说的大部分内容。但我发现消除它并不容易。
这是一个完全构成的例子,虽然强烈基于一些真实的代码 -
想象一下,我正在建立一个游戏。主游戏类需要构造一个执行3d渲染的“RenderDevice”对象。但是为了构造它,需要从配置文件加载一些设置。并且要绘制一个Window对象。还有一个记录器对象和一个内存池。我的代码现在将所有这些东西作为类成员放在构造函数很少的地方,然后使用相关参数在每个对象上调用init函数,如: -
// Much simplified code to make a point
Game::Game()
{
memoryPool_.init(10000000); // Amount of memory to allocate
logger_.init("logfile.txt", memoryPool_);
window_.init(2000, 1000); // Make a nice big window
renderDevice_.init(window_, logger_, memoryPool_);
}
对我而言似乎运作得相当好。但这是两个阶段。每个对象仅部分由其构造函数构造。所以我必须做这样的事情,而不是让代码“干净”。
Game::Game() :
memoryPool_(1000000),
logger_("logfile.txt", memoryPool_),
window_(2000, 1000),
renderDevice_(window_, logger_, memoryPool)
{
}
现在代码看起来对我来说相当丑陋,但也相当脆弱,因为初始化的顺序取决于它们在类中声明的顺序,而不是这里列出的顺序。随着越来越多的数据被添加到类中,它变得更糟。这里的对象只需要一些参数,但如果需要更多数据,这将很快失控。
它的优势在于每个对象都已准备好完成它的工作,但它看起来很难看,而且看起来相当脆弱且容易出错......
所以我的问题是我错过了这一点吗?有一个更好的方法吗?我应该停止担心,只是这样做,还是应该使用我的原始代码?或者我的整个设计在某种程度上是错误的,所以问题没有用?
基本上什么是最佳做法?
答案 0 :(得分:8)
现在代码对我来说似乎很难看
看起来不是那样的。也许你只是不习惯它?
相当脆弱,因为初始化的顺序取决于它们在类中声明的顺序,而不是此处列出的顺序
打开编译器的警告,它应该告诉你这两个订单之间的不匹配。 (我知道Clang有这个警告,我很确定GCC也有这个警告。对MSVC不太确定。)
此处的对象只需要一些参数,但如果需要更多数据,这将很快失控。
当您使用init()
时,我看不出有什么不同。
答案 1 :(得分:0)
根据我的经验,init函数用于可能存在错误的地方,并且不能使用/抛出异常。你提到的例子都有错误情况,似乎只是抛出异常,因为没有检查返回值。
我认为从Game中的init函数调用一组inits而不是从构造函数本身调用inits是个好习惯。