C ++ 11成员初始化列表与类内初始化程序?

时间:2014-12-08 05:37:26

标签: c++ c++11 initialization

在C ++ 11中初始化对象成员变量的这些方法有何不同?还有另外一种方法吗?哪种方式更好(性能)?:

class any {
  public:
    obj s = obj("value");
    any(){}
};

class any {
  public:
    obj s;
    any(): s("value"){}
};

感谢。

3 个答案:

答案 0 :(得分:37)

不,这些不一样

它们之间的区别与适用于直接初始化复制初始化的区别相同,这是微妙但通常非常混乱。

§12.6.2[class.base.init]:

  
      
  1. mem-initializer 中的表达式列表 braced-init-list 用于初始化指定的子对象(或者,在委托构造函数的情况下,完整的类对象)根据8.5的初始化规则进行直接初始化。 [...]

  2.   
  3. 在非委托构造函数中,如果给定的非静态数据成员或基类未由 mem-initializer-id 指定(包括没有<的情况) em> mem-initializer-list 因为构造函数没有 ctor-initializer )并且实体不是抽象类的虚拟基类(10.4),那么

         

    - 如果实体是具有大括号或等于初始化的非静态数据成员,则按照8.5 中的规定初始化;

  4.   

§8.5[dcl.init]:

  
      
  1. 形式出现的初始化      

    T x = a;

  2.         

    以及参数传递,函数返回,抛出异常(15.1),处理异常(15.3)和聚合成员初始化(8.5.1)称为复制初始化。< / p>

member-initializer-list 上初始化非静态数据成员遵循 direct-initialization 的规则,该规则不会创建需要的中间临时值移动/复制(如果没有 copy-elision 编译),数据成员的类型都不能是可复制/可移动的(即使复制被删除)。此外,直接初始化引入了显式上下文,而复制初始化是非显式的(如果为初始化选择的构造函数是explicit,该程序将无法编译。)

换句话说,如果将obj s = obj("value");声明为obj,则struct obj { obj(std::string) {} obj(const obj&) = delete; }; 语法将无法编译:

struct obj
{
    obj(std::string) {}
    explicit obj(const obj&) {}
};

或:

struct any
{
   std::atomic<int> a = std::atomic<int>(1); // ill-formed: non-copyable/non-movable
   std::atomic<int> b = 2; // ill-formed: explicit constructor selected
};

作为一个更切实的例子,虽然下面不会编译:

struct any
{
    std::atomic<int> a;
    std::atomic<int> b{ 2 };
    any() : a(1) {}
};

这个会:

obj s = obj("value");

  

哪种方式更好(性能)?

启用 copy-elision 后,两者的性能相同。禁用 copy-elision 后,在使用 copy-initialization 语法时,每次实例化时都会有一个额外的复制/移动构造函数调用(class any { public: obj s{ "value" }; any() {} }; 是其中之一)。


  

还有其他办法吗?

brace-or-equal-initializer 语法允许用户执行直接列表初始化

{{1}}

  

还有其他差异吗?

值得一提的其他一些差异是:

  1. Brace-or-equal-initializer 必须与类声明一起驻留在头文件中。
  2. 如果两者合并, member-initializer-list 优先于 brace-or-equal-initializer (即 brace-or-equal-初始化程序被忽略)。
  3. (仅限C ++ 11,直到C ++ 14)使用 brace-or-equal-initializer 的类违反了聚合类型的约束。
  4. 使用 brace-or-equal-initializer 语法,除 direct-list-initialization 之外,不可能执行直接初始化 >

答案 1 :(得分:7)

这两个例子都是等同的 虽然只有这种类型是可复制的或可移动的(自己检查)并且NRVO实际上已经完成(任何中途不错的编译器都会这样做)。

虽然如果你有许多构造函数和构造函数链接是不合适的,第一种方法将允许你不要重复自己。

此外,您可以使用该方法定义聚合,其默认值不同于自C ++ 14以来(部分)成员的聚合初始化。

答案 2 :(得分:3)

他们是一样的。

在性能方面,两者都不比另一方好,并且没有其他方法可以初始化它们。

类内初始化(在您的示例中的第一个)的好处是初始化的顺序是隐含的。在初始化列表中,您必须明确说明订单 - 如果订单不正确,编译器将发出无序初始化警告。

来自标准:

12.6.2.5
nonstatic data members shall be initialized in the order they were declared 
in the class definition

如果您的清单中的订单有误,GCC会抱怨:

main.cpp: In constructor 'C::C()':
main.cpp:51:9: warning: 'C::b' will be initialized after
main.cpp:51:6: warning:   'int C::a'

初始化列表的好处可能只是一种品味 - 列表是显式的,通常在源文件中。 In-class是隐式的(可以说是),通常在头文件中。