class 成员初始化的最佳实践
Best practice for class member initialization
假设我有一个 class Foo:
class Foo
{
public:
Foo();
~Foo();
private:
Bar* mBar;
}
并假设 'Bar' 是一个 class,其中有几个成员需要初始化。初始化该成员时使用的最佳做法是什么?
示例 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");
}
在 Foo
个构造函数中执行所有操作!
在构造函数中进行所有构造和初始化!这被称为 RAII: resource acquisition is initialisation。分配资源,在本例中 Foo
也应该初始化资源。这也使您的界面易于使用:Foo
不能 在不先至少构造一个 Foo
的情况下使用。构造完成后,假设它在其构造函数中初始化了所有内容,就可以使用了。如果您希望调用者也必须调用 Initialize
或 Set
函数,那么您会使 class 界面难以使用。如果 Initialize
没有首先被调用,Foo
将如何响应函数调用?突然之间,每个函数都需要具有以下内容:
bool Foo::SomeFunction()
{
if (!mBar->isInitialized())
return false;
// Do what we came here to do
return true;
}
现在调用者必须经常检查函数的 return 值。如果函数需要 return 一个值,但由于 mBar
未初始化而不得不指示错误怎么办?
一旦离开 RAII,你可以看到兔子洞变得很深:资源分配是初始化!
来自 Effective C++ Third Edition - Scott Myers:
Item 4: Make sure that objects are initialized before they're used
...
the responsibility for initialization falls on constructors. The rule there is simple: make sure that all constructors initialize everything in the object
其他建议
您还应该更喜欢使用构造函数初始化列表来构造成员:
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;
};
假设我有一个 class Foo:
class Foo
{
public:
Foo();
~Foo();
private:
Bar* mBar;
}
并假设 'Bar' 是一个 class,其中有几个成员需要初始化。初始化该成员时使用的最佳做法是什么?
示例 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");
}
在 Foo
个构造函数中执行所有操作!
在构造函数中进行所有构造和初始化!这被称为 RAII: resource acquisition is initialisation。分配资源,在本例中 Foo
也应该初始化资源。这也使您的界面易于使用:Foo
不能 在不先至少构造一个 Foo
的情况下使用。构造完成后,假设它在其构造函数中初始化了所有内容,就可以使用了。如果您希望调用者也必须调用 Initialize
或 Set
函数,那么您会使 class 界面难以使用。如果 Initialize
没有首先被调用,Foo
将如何响应函数调用?突然之间,每个函数都需要具有以下内容:
bool Foo::SomeFunction()
{
if (!mBar->isInitialized())
return false;
// Do what we came here to do
return true;
}
现在调用者必须经常检查函数的 return 值。如果函数需要 return 一个值,但由于 mBar
未初始化而不得不指示错误怎么办?
一旦离开 RAII,你可以看到兔子洞变得很深:资源分配是初始化!
来自 Effective C++ Third Edition - Scott Myers:
Item 4: Make sure that objects are initialized before they're used
...
the responsibility for initialization falls on constructors. The rule there is simple: make sure that all constructors initialize everything in the object
其他建议
您还应该更喜欢使用构造函数初始化列表来构造成员:
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;
};