类成员初始化的最佳实践

Best practice for class member initialization

本文关键字:最佳 成员 初始化      更新时间:2023-10-16

假设我有一个类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"也有一个设置方法

Foo::Foo
{
    mBar = new Bar();
}
Foo::Initialize()
{
    mBar->SetMembers("some", "required", "members");
}

Foo构造函数中执行所有操作

在构造函数中进行所有构造和初始化!这被称为RAII:资源获取是初始化。在分配资源时,在这种情况下,Foo也应该初始化资源。这也使您的界面易于使用:如果不首先构造Foo,就不能使用Foo。一旦构造好,假设它初始化了构造函数中的所有内容,就可以使用了。如果您希望调用者也必须调用InitializeSet函数,那么您的类接口就很难使用。如果Initialize尚未被首次调用,Foo将如何响应函数调用?突然间,每个功能都需要具备以下功能:

bool Foo::SomeFunction()
{
    if (!mBar->isInitialized())
        return false;
    // Do what we came here to do
    return true;
}

现在,调用者必须不断地检查函数的返回值。如果函数需要返回一个值,但由于mBar没有初始化而不得不指示错误,该怎么办?

你可以看到,一旦离开RAII,兔子洞就会很深:资源分配就是初始化!

从有效的C++第三版-斯科特迈尔斯:

第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;
};