成员初始化程序列表和非静态数据成员上的默认成员初始值设定项之间的区别是什么?

时间:2016-04-13 13:38:07

标签: c++ class variables init

我想了解使用一种形式而不是另一种形式(如果有的话)的区别。

Code 1(直接在变量上初始化):

#include <iostream>

using namespace std;

class Test 
{
public:
    Test() {
        cout<< count;
    }

    ~Test();

private:
    int count=10;
};

int main()
{
    Test* test = new Test();
}

Code 2(构造函数初始化列表初始化):

#include <iostream>

using namespace std;

class Test 
{
public:
    Test() : count(10) {
        cout<< count;
    }

    ~Test();

private:
    int count;
};

int main()
{
    Test* test = new Test();
}

语义上有什么不同,或者它只是语法吗?

5 个答案:

答案 0 :(得分:6)

在C ++核心指南中(见下面的注释1),Guideline C.48推荐了第一种方法(类内初始化器)。提供的推理是:

  

明确指出在所有构造函数中都应使用相同的值。避免重复。避免维护问题。它导致最短和最有效的代码。

事实上,如果你的构造函数除了初始化成员变量之外什么也没做,就像在你的问题中那样,那么Guideline C.45仍然更加坚定,并且肯定会使用类内初始化器。它解释了

  

使用类内成员初始值设定项可让编译器为您生成函数。编译器生成的函数可以更有效。

即使我没有编写编译器,我也不会与Stroustrup,Sutter以及他们的几百位朋友和同事争论,因此我无法证明它更有效率。尽可能使用课堂初始化程序。

  1. 如果您不熟悉指南,请按照链接查看示例代码和更多说明。

答案 1 :(得分:5)

我能想到的差异是member initializer list优先于default member initializer

  

通过默认的成员初始化程序,它只是一个大括号或   等于使用的成员声明中包含的初始化程序   如果在成员初始化列表中省略了该成员。

     

如果成员具有默认成员初始值设定项并且也出现在   构造函数中的成员初始化列表,默认成员   初始化程序被忽略。

例如:

class Test 
{
public:
    Test() {}  // count will be 10 since it's omitted in the member initializer list
    Test(int c) : count(c) {} // count's value will be c, the default member initializer is ignored. 
private:
    int count = 10;
};

答案 2 :(得分:4)

成员初始化

在这两种情况下,我们都在讨论member initialization。 请记住,成员是在班级的sequence in which they are declared中初始化的。

代码2:成员初始化列表

在第二个版本中:

Test() : count(10) {

: count(10)是构造函数初始值设定项(ctor-initializer),count(10)成员初始值设定项,是成员初始值设定项列表的一部分。我喜欢将此视为真实的&#39;或初始化发生的主要方式,但它不确定初始化的顺序。

代码1:默认成员初始值设定项

在第一个版本中:

private:
    int count=10;

count有一个默认成员intitializer 。这是后备选项。如果构造函数中不存在成员初始值设定项,它将用作成员初始值设定项,但在类中,将确定初始化成员序列。

从标准的 12.6.2初始化基础和成员,第10项部分开始:

  

如果给定的非静态数据成员同时具有   brace-or-equal-initializer和mem-initializer,初始化   由mem-initializer指定执行,以及非静态数据   member的brace-or-equal-initializer被忽略。 [例子:给定

struct A {
int i = / some integer expression with side effects / ;
A(int arg) : i(arg) { }
// ...
};
     

A(int)构造函数只需将i初始化为arg的值,   并且i的支撑或平衡初始化器中的副作用不会采取   地点。 - 例子]

要记住的其他事情是,如果你引入一个非静态数据成员初始值设定项,那么结构将不再被认为是C ++ 11中的聚合,但这已经是updated for C++14

差异

  

使用一种形式而不是另一种形式的差异(如果   有)。

  • 区别在于两个选项的优先级。直接指定的构造函数初始值设定项具有优先权。在这两种情况下,我们都会通过不同的路径获得成员初始化程序。
  • 最好使用默认成员初始值设定项,因为
    • 然后编译器可以使用该信息为您生成构造函数的初始化列表,并且它可能能够进行优化。
    • 您可以在一个地方按顺序查看所有默认设置。
    • 减少重复。然后,您只能将异常放在手动指定的成员初始化列表中。

答案 3 :(得分:2)

代码没有区别。如果您将有多个构造函数重载并且多个计数将是10,那么差异就会出现。对于第一个版本,您将需要更少的写入。

class Test 
{
public:
    Test() = default;
    Test(int b) : b(b) {} // a = 1, c = 3

    ~Test();

private:
    int a = 1;
    int b = 2;
    int c = 3;
};

与第二个版本相反,上面的代码如下所示:

class Test 
{
public:
    Test() : a(1), b(2), c(3) {}
    Test(int b) : a(1), b(b), c(3) {}

    ~Test();

private:
    int a;
    int b;
    int c;
};

随着更多成员变量,差异越大。

答案 4 :(得分:1)

当你在成员的声明旁边初始化时,这仅在C ++ 11以后才有效,所以如果你在C ++ 98/03中,你就完全不能这样做。

如果值永远不会改变,您可以选择将其改为constexpr static,然后编译器将被要求不为该值使用任何额外的存储空间(只要您不定义它)并且无论在何处使用该值,都可以立即使用常量传播。

使用by-declaration语法的一个缺点是它必须位于标头中,这将导致每次要更改其值时重新编译包含标头的所有翻译单元。如果这需要很长时间,那可能是不可接受的。

另一个区别是使用成员初始化列表允许您更改每个构造函数的值,而使用by-declaration版本只允许您为所有构造函数指定一个值(尽管您可以覆盖此值...但是我和#39; d个人避免这种情况,因为它可能会让人感到困惑!)。

顺便说一句,此处无需使用new来创建Test的实例。当人们从其他语言中学习语言并且我想让你知道时,这是一个常见的错误。在您的示例之外,当然有许多用途。