简体   繁体   English

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

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

I'd like to understand what's the differences of using one form rather than the other (if any). 我想了解使用一种形式而不是另一种形式(如果有的话)的区别。

Code 1 (init directly on variables): 代码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 (init with initialization list on constructor): 代码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();
}

Is there any difference in the semantics, or it is just syntactic? 语义有什么不同,或者它只是语法吗?

In the C++ Core Guidelines (see note 1 below), Guideline C.48 recommends the first approach (in-class initializers.) The reasoning provided is: 在C ++核心指南中(参见下面的注释1), 指南C.48推荐了第一种方法(类内初始化器)。提供的推理是:

Makes it explicit that the same value is expected to be used in all constructors. 明确表示在所有构造函数中使用相同的值。 Avoids repetition. 避免重复。 Avoids maintenance problems. 避免维护问题。 It leads to the shortest and most efficient code. 它导致最短和最有效的代码。

In fact if your constructor does nothing but initialize member variables, as in your question, then Guideline C.45 is firmer still, saying to use in-class initializers for sure. 事实上,如果您的构造函数除了初始化成员变量之外什么也没做,就像在您的问题中那样,那么Guideline C.45仍然更加坚定,并且肯定会使用类内初始值设定项。 It explains that 它解释了这一点

Using in-class member initializers lets the compiler generate the function for you. 使用类内成员初始值设定项可让编译器为您生成函数。 The compiler-generated function can be more efficient. 编译器生成的函数可以更高效。

I am not going to argue with Stroustrup, Sutter, and several hundred of their friends and colleagues even if I haven't written a compiler so I can't prove it's more efficient. 即使我没有编写编译器,我也不会与Stroustrup,Sutter和他们的几百个朋友和同事争论,因此我无法证明它更有效率。 Use in-class initializers wherever you can. 尽可能使用类内初始值设定项。

  1. If you're not familiar with the guidelines do follow the links to see sample code and more explanations. 如果您不熟悉指南,请按照链接查看示例代码和更多说明。

Member initialization 成员初始化

In both cases we are talking about member initialization . 在这两种情况下,我们都在谈论成员初始化 Keep in mind that the members are initialized in the sequence in which they are declared in the class. 请记住,成员按照在类中声明顺序进行初始化。

Code 2: Member initializer list 代码2:成员初始化列表

In the second version: 在第二个版本中:

Test() : count(10) {

: count(10) is a constructor initializer ( ctor-initializer ) and count(10) is a member initializer as part of the member initializer list. : count(10)是构造函数初始值设定项( ctor-initializer ), count(10)成员初始值设定项,作为成员初始值设定项列表的一部分。 I like to think of this as the 'real' or primary way that the initialization happens, but it does not determine the sequence of initialization. 我喜欢将此视为初始化发生的“真实”或主要方式,但它并不确定初始化的顺序。

Code 1: Default member initializer 代码1:默认成员初始化程序

In the first version: 在第一个版本中:

private:
    int count=10;

count has a default member intitializer . count有一个默认成员intitializer It is the fallback option. 这是后备选项。 It will be used as a member initializer if none is present in the constructor, but in the class the sequence of members for initialization is determined. 如果构造函数中不存在,它将用作成员初始值设定项 ,但在类中,将确定用于初始化的成员序列。

From section 12.6.2 Initializing bases and members, item 10 of the standard: 从第12.6.2初始化基地和成员,标准的第10项

If a given non-static data member has both a brace-or-equal-initializer and a mem-initializer, the initialization specified by the mem-initializer is performed, and the non-static data member's brace-or-equal-initializer is ignored. 如果给定的非静态数据成员同时具有大括号或等号初始化器和mem-initializer,则执行mem-initializer指定的初始化,并且非静态数据成员的大括号或等于初始值为忽略。 [ Example: Given [例子:给定

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

the A(int) constructor will simply initialize i to the value of arg, and the side effects in i's brace-or-equalinitializer will not take place. A(int)构造函数将简单地将i初始化为arg的值,并且不会发生i的brace-or-equalinitializer中的副作用。 —end example ] - 末端的例子]

Something else to keep in mind would be that if you introduce a non-static data member initializer then a struct will no longer be considered an aggregate in C++11, but this has been updated for C++14 . 要记住的其他事情是,如果你引入一个非静态数据成员初始化程序,那么一个结构将不再被认为是C ++ 11中的聚合,但是已经针对C ++ 14进行了更新


Differences 差异

what's the differences of using one form rather than the other (if any). 使用一种形式而不是另一种形式(如果有的话)的区别是什么。

  • The difference is the priority given to the two options. 区别在于两个选项的优先级。 A constructor initializer, directly specified, has precedence. 直接指定的构造函数初始值设定项具有优先权。 In both cases we end up with a member initializer via different paths. 在这两种情况下,我们都会通过不同的路径获得成员初始化程序。
  • It is best to use the default member initializer because 最好使用默认成员初始值设定项,因为
    • then the compiler can use that information to generate the constructor's initializer list for you and it might be able to optimize. 然后编译器可以使用该信息为您生成构造函数的初始化列表,并且它可能能够进行优化。
    • You can see all the defaults in one place and in sequence. 您可以在一个地方按顺序查看所有默认值。
    • It reduces duplication. 它减少了重复。 You could then only put the exceptions in the manually specified member initializer list. 然后,您只能将异常放在手动指定的成员初始化列表中。

The difference I can think of is that member initializer list is prior to default member initializer . 我能想到的差异是成员初始化列表默认成员初始化程序之前。

Through a default member initializer, which is simply a brace or equals initializer included in the member declaration, which is used if the member is omitted in the member initializer list. 通过默认成员初始值设定项,它只是成员声明中包含的大括号或等于初始值设定项,如果在成员初始值设定项列表中省略该成员,则使用该初始值设定项。

If a member has a default member initializer and also appears in the member initialization list in a constructor, the default member initializer is ignored. 如果成员具有默认成员初始值设定项并且也出现在构造函数的成员初始化列表中,则忽略默认成员初始值设定项。

For example: 例如:

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

There is no difference in the code. 代码没有区别。 The difference would come if you would be would have more than one constructor overload and in more than one count would be 10. With the first version you would have less writing to do. 如果您将有多个构造函数重载并且多个计数将是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;
};

As opposed to the second version where the above code would look like this: 与第二个版本相反,上面的代码如下所示:

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

The difference gets bigger with more member variables. 随着更多成员变量,差异越大。

When you initialise next to the declaration of the member, this is valid only in C++11 onwards, so if you're in C++98/03 you outright cannot do this. 当你在成员的声明旁边初始化时,这仅在C ++ 11以后才有效,所以如果你在C ++ 98/03中,你就完全不能这样做了。

If the value never changes, you could choose to make this a constexpr static instead and the compiler would then be required to not use any extra storage for the value (so long as you don't define it) and instant use constant propagation wherever the value is used instead. 如果值永远不会改变,你可以选择将它作为constexpr static代替,然后编译器将被要求不为该值使用任何额外的存储空间(只要你没有定义它)并且即时使用常量传播而是使用值。

One disadvantage of using the by-declaration syntax is that it must be in the header, which will result in a recompile of all translation units that include the header every time you want to change its value. 使用by-declaration语法的一个缺点是它必须位于标头中,这将导致每次要更改其值时重新编译包含标头的所有转换单元。 If this takes a long time, that might be unacceptable. 如果这需要很长时间,那可能是不可接受的。

Another difference is that using the member initialisation list lets you change the value for each constructor, whilst using the by-declaration version only allows you to specify one value for all constructors (although you could overwrite this value ... but I'd personally avoid this as it could get quite confusing!). 另一个区别是使用成员初始化列表允许您更改每个构造函数的值,而使用by-declaration版本只允许您为所有构造函数指定一个值(尽管您可以覆盖此值...但我个人避免这种情况,因为它可能会让人感到困惑!)


As an aside, there's no need to use new here to create an instance of Test . 顺便说一句,没有必要在这里使用new来创建Test实例。 This is a common mistake when people come to the language from other languages and I wanted to make you aware. 当人们从其他语言中学习语言并且我想让你知道时,这是一个常见的错误。 There are of course many uses for doing this outside of your example. 在您的示例之外,当然有很多用途。

暂无
暂无

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

相关问题 是否允许在默认成员初始化程序中调用非静态成员 function? - Is it allowed to call a non-static member function in a default member initializer? 成员初始值设定项不命名非静态数据成员 - Member initializer does not name a non-static data member 部分聚合初始化和非静态数据成员初始化程序 - Partial Aggregate Initialization and Non-static Data Member Initializer 非静态数据成员初始值设定项中lambda函数的分段错误 - Segmentation fault for lambda function in non-static data member initializer 来自另一个非静态的非静态成员初始化程序 - Non-static member initializer from another non-static const static 数据成员的初始值设定项是否被视为默认成员初始值设定项? - Is the initializer for a const static data member considered a default member initializer? 带有初始化程序的静态数据成员 - Static data member with initializer 当初始值设定项是基类名称时出现错误“初始值设定项未命名非静态数据成员或基类” - Error 'initializer does not name a non-static data member or base class' when the initializer is the base class name c2797成员初始化程序列表中的列表初始化或未实现的非静态数据成员初始化程序 - c2797 List initialization inside member initializer list or non static data member initializer not implemented 成员初始化程序“ SuperClass”未命名非静态数据成员或基类 - member initializer 'SuperClass' does not name a non-static data member or base class
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM