繁体   English   中英

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

[英]What's the differences between member initializer list and default member initializer on non-static data member?

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

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

#include <iostream>

using namespace std;

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

    ~Test();

private:
    int count=10;
};

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

代码2 (init与构造函数上的初始化列表):

#include <iostream>

using namespace std;

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

    ~Test();

private:
    int count;
};

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

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

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

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

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

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

即使我没有编写编译器,我也不会与Stroustrup,Sutter和他们的几百个朋友和同事争论,因此我无法证明它更有效率。 尽可能使用类内初始值设定项。

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

成员初始化

在这两种情况下,我们都在谈论成员初始化 请记住,成员按照在类中声明顺序进行初始化。

代码2:成员初始化列表

在第二个版本中:

Test() : count(10) {

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

代码1:默认成员初始化程序

在第一个版本中:

private:
    int count=10;

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

从第12.6.2初始化基地和成员,标准的第10项

如果给定的非静态数据成员同时具有大括号或等号初始化器和mem-initializer,则执行mem-initializer指定的初始化,并且非静态数据成员的大括号或等于初始值为忽略。 [例子:给定

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

A(int)构造函数将简单地将i初始化为arg的值,并且不会发生i的brace-or-equalinitializer中的副作用。 - 末端的例子]

要记住的其他事情是,如果你引入一个非静态数据成员初始化程序,那么一个结构将不再被认为是C ++ 11中的聚合,但是已经针对C ++ 14进行了更新


差异

使用一种形式而不是另一种形式(如果有的话)的区别是什么。

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

我能想到的差异是成员初始化列表默认成员初始化程序之前。

通过默认成员初始值设定项,它只是成员声明中包含的大括号或等于初始值设定项,如果在成员初始值设定项列表中省略该成员,则使用该初始值设定项。

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

例如:

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

代码没有区别。 如果您将有多个构造函数重载并且多个计数将是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;
};

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

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

如果值永远不会改变,你可以选择将它作为constexpr static代替,然后编译器将被要求不为该值使用任何额外的存储空间(只要你没有定义它)并且即时使用常量传播而是使用值。

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

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


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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM