默认成员值的最佳实践

115

在编写C++11代码时,在类的头文件中设置类成员的默认值是一个好的实践吗?

还是最好在类的构造函数中设置默认值?

编辑:

我的意思是:

foo.h

#include <string>

using std::string;

class Foo{
    private:
        string greet = "hello";
    public:
        Foo();
};

foo.cpp(当然需要必要的头文件,但不包括类内初始化):

Foo::Foo(){
    greet = "hello";
}

哪一个更好,为什么?


2
头文件中的意思是什么?C++11中类成员初始化?提供两种情况的代码示例可以帮助说明。 - chris
1
@chris 更新了代码示例。 - Paul
这两个示例执行不同的操作,因此第一个更好。搜索“成员初始化列表”。 - K-ballo
6
头文件中应避免使用using语句。 - vlad_tepesch
2
我知道有点晚了 - 你应该使用构造函数初始化列表:Foo::Foo():greet("hello") { } - 这避免了默认初始化+赋值,而是采用直接按值初始化。对于复杂类型,这可能会产生显着的差异,更重要的是:某些类型(引用、常量成员、非默认可构造的类型)只能通过这种方式初始化(在构造函数中,除了分配默认值)。 - Aconcagua
《C++对象模型内幕》一书中的解释,作者是Stanley B. Lippman。请参考链接:https://stackoverflow.com/questions/51183155/initializer-list-vs-constructor-assignment-vs-variable-defining - undefined
3个回答

124
如果一个类成员总是用相同的初始值进行初始化,那么应该将初始化程序设置为内联,以避免重复。如果初始值依赖于构造函数,则应将其放入构造函数初始化列表中。(而且永远不要像您所做的那样使用赋值。)
示例:
class Foo
{
    bool done = false;   // always start like this
    int qty;
    Bar * p;

public:
    Foo()                        : qty(0),              p(nullptr)    { }
    Foo(int q, Bar * bp)         : qty(q),              p(bp)         { }
    explicit Foo(char const * s) : qty(std::strlen(s)), p(new Bar(s)) { }

    // ...
};

在这个假设的例子中,成员变量done始终需要以false的状态开始,因此最好在内联方式下编写初始化程序。另外两个成员变量qtyp可以在三个不同的构造函数中以不同的方式进行初始化,因此它们在构造函数的初始化列表中进行初始化。

一个有趣的事实:请注意,提供内联初始化器会防止您的类具有平凡的默认构造函数


9
你也可以使用构造函数委托来避免冗余。 - Johannes Schaub - litb
28
“在你所做的方式中永远不要使用赋值符号。”我认为解释为什么不能这样做会是你回答的一个很好的补充。 - cp.engr
3
因为通常应该进行初始化而不是重新赋值。 - Kerrek SB
2
@KerrekSB,Paul使用的赋值和你使用的赋值有什么区别?两者都将成员变量赋值为一个值。 - Jossie Calderon
1
@KerrekSB 在你的示例的第3行,"done = false"算作一次赋值吗? - user2674487
显示剩余2条评论

13

这要取决于你是否需要与旧版C++编译器保持兼容。当你不使用C++11时,你必须在构造函数中初始化大多数成员(所有非静态成员)。 此外,许多人主张即使这意味着显式调用默认构造函数,也应显式初始化每个成员。 通常情况下,你应该将实现细节放在cpp文件中而不是头文件中,因此一个示例可能是:

Example:
//foo.h

class Foo{
public: 
  Foo();
private:
  std::vector<int> vect;
};

//foo.cpp

Foo::Foo():vect(){
}
在C++11中,你有更多的选择,使用类成员初始化器尤其方便,特别是当你有多个属性时。这里有一个很好的链接,提供更多信息:http://www.stroustrup.com/C++11FAQ.html#member-init 编辑后: 根据您的代码,您正在使用C++11。据我所知,关于新功能的良好实践方面只有很少的信息,但在我的观点中,使用类成员初始化器非常方便,可以将初始化集中在一个地方,从而降低复杂性和键入次数。

是的,我更新了我的问题以更好地反映我的意思,我在“减少复杂性和打字”的观点上大多同意您的看法。 - Paul

5

在头文件中进行初始化的主要优点是使代码更加本地化和易于理解。它还可以节省一些打字。

我认为主要缺点是需要包含更多的头文件才能访问构造函数。简单的前向声明不足以支持,这会使编译时间更长。


编译时间变长 - undefined
从2012年以来,我们在处理器速度方面取得了长足的进步,但是重新编译需要30秒还是30分钟的差异仍然可能产生很大的影响。代码库的规模无疑是需要特别关注的主要因素。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接