说我有一个班级Foo:
class Foo
{
public:
Foo();
~Foo();
private:
Bar* mBar;
}
并假设' Bar'是一个需要初始化的少数成员的类。初始化该成员时使用的最佳做法是什么?
示例1:在Foos构造函数中执行所有操作:
Foo::Foo()
{
mBar = new Bar("some", "required", "members");
}
示例2:创建单独的初始化函数:
Foo::Foo()
{
}
Foo::Initialize()
{
mBar = new Bar("some", "required", "members");
}
示例3:两部分初始化,假设' Bar'还有一个set方法
Foo::Foo
{
mBar = new Bar();
}
Foo::Initialize()
{
mBar->SetMembers("some", "required", "members");
}
答案 0 :(得分:5)
Foo
构造函数中执行所有操作!在构造函数中完成所有构造和初始化!这称为RAII: resource acquisition is initialisation。分配资源,在这种情况下,Foo
也应该初始化资源。这也使您的界面易于使用:Foo
无法,而无需先构建Foo
。一旦构造,假设它初始化其构造函数中的所有内容,它就可以使用了。如果您希望呼叫者也必须调用Initialize
或Set
函数,那么您的类界面很难使用。如果Foo
尚未首次调用,Initialize
将如何响应函数调用?突然,每个功能都需要具备以下功能:
bool Foo::SomeFunction()
{
if (!mBar->isInitialized())
return false;
// Do what we came here to do
return true;
}
现在,来电者必须经常检查您的功能的返回值。如果函数需要返回一个值,但由于mBar
未初始化而必须指示错误,该怎么办?
一旦离开RAII,你就可以看到兔子洞很深:资源分配是初始化!
来自Effective C++ Third Edition - Scott Myers:
第4项:确保在使用对象之前对其进行初始化
...
初始化的责任落在构造函数上。规则很简单:确保所有构造函数初始化对象中的所有内容
您还应该使用构造函数初始化列表来构造成员:
Foo::Foo() : mBar(new mBar("some", "required", "members"))
{
}
否则您将制作不必要的副本。我还假设您有充分的理由使用指针。正如@Niels van Eldik在评论中指出的那样,你应该使用对象,除非你真的需要使用指针,在这种情况下你应该使用最适合你需要的智能指针(在这种情况下,我会猜测并说出来) std::unique_ptr
:
class Foo
{
public:
Foo() : mBar(std::make_unique<Bar>("some", "required", "members"))
{
}
private:
std::unique_ptr<Bar> mBar;
};