我正在审核一些代码,我看到了很多这样的代码:
class Foo
{
public:
Foo()
{
// 'nuffin
}
void init()
{
// actual construction code
}
} ;
我能看到的唯一优势是,如果你在不使用指针的情况下创建一个Foo,并且想要延迟它的构造代码,那么你可以。
这是一个好主意还是一个坏主意?
答案 0 :(得分:4)
我不喜欢它。在我看来,在建造之后,一个物体应该......好......构造。该代码使其处于无效状态,这几乎是 1 从来都不是一件好事。
1 插入黄鼠狼单词以解决无法预料的情况。
答案 1 :(得分:3)
总的来说,我同意这是值得避免的。但到目前为止,没有解决的问题是初始化可能失败的可能性。构造函数无法失败,因此如果构造函数分配内存,或打开文件,或执行其他可能失败的操作,则需要一种方法来告诉调用者发生了错误。如果在构造函数中进行初始化,则需要有一个标志,指示初始化是否成功,然后确保调用者检查该标志。
如果你有一个单独的init()例程,必须在其他任何工作之前调用,调用者更有可能检查那个返回代码而不是在创建后调用didInitializationSucceed()
方法对象
答案 2 :(得分:2)
如果类上有依赖于对象处于某种初始化状态的方法,则通常认为两阶段构造是个坏主意。通常,我更喜欢保证对象处于良好状态的构造函数,或者如果无法完成(可能是因为构造函数的某些参数无效),抛出异常,所以从来没有类的实例是处于糟糕状态。
要求对象的消费者记住调用init()
是一个坏主意,因为他们不会。
答案 3 :(得分:1)
我相信构造函数基本上也应该执行init()部分。除非对象是完全构造的,否则不应使用它。
此外,在构造函数中初始化允许您使用RAII。 RAII的基本点是通过本地对象表示资源,在构造函数中初始化,以便本地对象的析构函数将释放资源。这样,程序员就不会忘记释放资源。
答案 4 :(得分:1)
可能适用的一种情况是'Foo'是另一个类的属性,并且在完成父类之前无法完全构造。只有这样才能'Foo'被'填补'。
答案 5 :(得分:1)
在某些语言(读取:C ++)中,您无法从另一个构造函数中调用构造函数,因此如果您需要多个构造函数的公共部分,则需要将其放在单独的方法中,并且我已经看到了名称init ()用于此。但那不是你在说什么?
答案 6 :(得分:1)
如果我实例化基于数据库调用的对象,则使用contructor和init。所以,如果我需要一个空对象,以便我可以填充它然后将其保存到数据库,我构造没有参数,不要调用init()。然而,如果我需要从数据库中检索对象成员,我将contruct($param)
并将$ param传递给init($param)
。
答案 7 :(得分:0)
通常,源代码应该尽可能简单,并且您的示例在没有上下文的情况下显示,因此它比必要的更复杂,因此您的示例是个坏主意。
然而,可能存在一些语义上下文,其中可能有意义的是能够传递未初始化的对象 - 例如,如果上下文要求容器具有对象但是您不希望在以后初始化它们,因为初始化很慢和/或可能不需要对象。在这些情况下,额外的复杂性可能使其他更简单。
答案 8 :(得分:0)
不同之处在于,在调用超类的构造函数之后但在本地类构造函数中执行任何代码之前,初始化发生。因此,这实际上取决于您的需求。
答案 9 :(得分:0)
虽然它不能被认为是构建对象的正常或首选方式,但在某些情况下可能是一种方法。例如,您可能需要构造一个对象只是为了表明它的存在(在某些列表中,count确实很重要等),但是只有在第一次使用这个特定对象时才初始化它,因为初始化整个对象集合会花很多时间。
在这种情况下,最好通过包含isInitialized()
之类的方法来揭示对象可能未初始化的事实。另外,您可以将初始化转移到另一个线程,以免阻塞应用程序的主线程。