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 的情况下使用。构造完成后,假设它在其构造函数中初始化了所有内容,就可以使用了。如果您希望调用者也必须调用 InitializeSet 函数,那么您会使 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;
};