默认值,值和零初始化混乱

Gab*_*iel 81 c++ c++11 c++03 c++98 c++14

我对value-&default-&zero-initialization非常困惑.特别是当他们参与不同的标准C++ 03C++ 11(和C++ 14)时.

我引用并试图在这里扩展一个非常好的答案Value-/Default-/Zero-初始化C++ 98C++ 03,以使其更加通用,因为它可以帮助很多用户,如果有人可以帮助填写需要差距,以便对何时发生的情况有一个很好的概述?

通过示例的全面见解简而言之:

有时新运算符返回的内存将被初始化,有时它不会取决于您正在新建的类型是POD(普通旧数据),还是它是一个包含POD成员且正在使用的类编译器生成的默认构造函数.

  • C++ 1998中,有两种类型的初始化:默认初始化
  • C++ 2003第三种类型的初始化中,添加了值初始化.
  • C++ 2011/C++ 2014中,仅添加了列表初始化,并且value-/default-/zero-initialization的规则稍有改变.

假设:

struct A { int m; };                     
struct B { ~B(); int m; };               
struct C { C() : m(){}; ~C(); int m; };  
struct D { D(){}; int m; };             
struct E { E() = default; int m;} /** only possible in c++11/14 */  
struct F {F(); int m;}  F::F() = default; /** only possible in c++11/14 */
Run Code Online (Sandbox Code Playgroud)

在C++ 98编译器中,应该发生以下情况:

  • new A - 不确定值(A是POD)
  • new A()- 零初始化
  • new B - 默认构造(B::m未初始化,B非POD)
  • new B()- 默认构造(B::m未初始化)
  • new C - 默认构造(C::m零初始化,C非POD)
  • new C()- 默认构造(C::m零初始化)
  • new D - 默认构造(D::m未初始化,D非POD)
  • new D()- 默认构造?(D::m未初始化)

在符合C++ 03的编译器中,事情应该像这样工作:

  • new A - 不确定值(A是POD)
  • new A() - value-initialize A,这是零初始化,因为它是POD.
  • new B - default-initializes(B::m保留未初始化,B非POD)
  • new B() - value-initializes B对所有字段进行零初始化,因为它的默认ctor是编译器生成的而不是用户定义的.
  • new C - default-initializes C,调用默认的ctor.(C::m零初始化,C非POD)
  • new C() - value-initializes C,调用默认的ctor.(C::m零初始化)
  • new D - 默认构造(D::m未初始化,D非POD)
  • new D() - 值初始化D?,调用默认的ctor(D::m未初始化)

斜体值和?是不确定的,请帮助纠正这个:-)

在符合C++ 11的编译器中,事情应该如下:

??? (如果我从这里开始请帮忙,无论如何都会出错)

在符合C++ 14的编译器中,事情应该像这样工作: ??? (请帮助,如果我从这里开始它将会出错) (根据答案草案)

  • new A - default-initializes A,编译器gen.ctor,(leavs A::muninitialized)(A是POD)
  • new A() - value-initializes A,这是从[dcl.init]/8中的 2. point开始的零初始化

  • new B - default-initializes B,编译器gen.ctor,(leavs B::muninitialized)(B非POD)

  • new B() - value-initializes B对所有字段进行零初始化,因为它的默认ctor是编译器生成的而不是用户定义的.
  • new C - default-initializes C,调用默认的ctor.(C::m零初始化,C非POD)
  • new C() - value-initializes C,调用默认的ctor.(C::m零初始化)
  • new D - default-initializes D(D::m未初始化,D非POD)
  • new D() - value-initializes D,调用默认的ctor(D::m未初始化)
  • new E - default-initializes E,调用comp.根.构造函数.(E::m未初始化,E是非POD)
  • new E() -值初始化E,其零初始化E自2点在[dcl.init]/8)
  • new F - default-initializes F,调用comp.根.构造函数.(F::m未初始化,F非POD)
  • new F() - value-initializes F,默认初始化为 F 1.指向[dcl.init]/8(Fctor函数是用户提供的,如果它是用户声明的,并且在第一次声明时未明确默认或删除.链接)

Col*_*mbo 24

C++ 14指定用new[expr.new]/17(C++ 11中的[expr.new]/15)创建的对象的初始化,并且该注释不是注释,而是当时的规范文本):

新表达式创建类型的对象T如下初始化该对象:

  • 如果省略new-initializer,则默认初始化对象(8.5).[ 注意:如果未执行初始化,则对象具有不确定的值.- 结束说明 ]
  • 否则,根据8.5的初始化规则解释new-initializer以进行直接初始化.

默认初始化在[dcl.init]/7中定义(在C++ 11中为/ 6,并且措辞本身具有相同的效果):

默认初始化的类型的对象T是指:

  • 如果T是一个(可能是cv限定的)类类型(第9节),T则调用默认构造函数(12.1)(如果T没有默认构造函数或重载解析(13.3),则初始化是错误的,导致歧义或在从初始化的上下文中删除或无法访问的函数);
  • 如果T是数组类型,则每个元素都是默认初始化的 ;
  • 否则,不执行初始化.

从而

  • new A只会导致A调用默认构造函数,它不会初始化m.不确定的价值.应该是相同的new B.
  • new A() 根据[dcl.init]/11(C++ 11中的/ 10)解释:

    初始化器为空的括号集的对象,即(),应进行值初始化.

    现在考虑[dcl.init]/8(C++ 11†中的/ 7):

    值初始化类型的对象T意味着:

    • if T是一个(可能是cv限定的)类类型(第9条),没有默认构造函数(12.1)或者是用户提供或删除的默认构造函数,那么该对象是默认初始化的;
    • 如果T是没有用户提供或删除的默认构造函数的(可能是cv限定的)类类型,则对象将进行零初始化并检查默认初始化的语义约束,如果T具有非平凡的默认构造函数,该对象是默认初始化的;
    • 如果T是数组类型,则每个元素都是值初始化的;
    • 否则,该对象被零初始化.

    因此new A()将零初始化m.这对于AB.

  • new C并且new C()将再次默认初始化对象,因为最后一个引用的第一个项目符号点适用(C具有用户提供的默认构造函数!).但是,显然,现在m在两种情况下都在构造函数中初始化.


†好吧,这一段在C++ 11中的措辞略有不同,不会改变结果:

值初始化类型的对象T意味着:

  • 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则T 调用默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
  • 如果T是没有用户提供的构造函数的(可能是cv限定的)非联合类类型,则该对象是零初始化的,如果T隐式声明的默认构造函数是非平凡的,则调用该构造函数.
  • 如果T是数组类型,则每个元素都是值初始化的;
  • 否则,该对象被零初始化.


Abh*_*jit 12

以下答案扩展了答案/sf/answers/43428171/,它将作为C++ 98和C++ 03的参考

引用答案

  1. 在C++ 1998中,有两种类型的初始化:零和默认
  2. 在C++ 2003第三种类型的初始化中,添加了值初始化.

C++ 11(参考n3242)

初始化器

8.5 Initializers [dcl.init]指定变量POD或非POD可以初始化为brace-or-equal-initializer,它可以是 braced-init-listinitializer-clause,统称为 brace-or-equal-初始化器或使用(表达式列表).在C++ 11之前,只支持(expression-list)initializer-clause,尽管initializer-clause比C++ 11中的更受限制.在C++ 11中,initializer-clause现在支持除了C++ 03中的赋值表达式之外的braced-init-list.以下语法总结了新支持的子句,其中部分为粗体是在C++ 11标准中新添加的.

initializer:
    brace-or-equal-initializer
    (expression-list)
brace-or-equal-initializer:
    = initializer-clause
    braced-init-list
initializer-clause:
    assignment-expression
    braced-init-list
initializer-list:
    initializer-clause ... opt
    initializer-list,initializer-clause ... opt**
braced-init-list:
    {initializer-list,opt}
    {}

初始化

与C++ 03一样,C++ 11仍支持三种初始化形式


注意

在C++ 11中添加了以粗体突出显示的部分,并且从C++ 11中删除了被删除的部分.

  1. 初始化程序类型:8.5.5 [dcl.init] _zero-initialize_

在以下情况下执行

  • 具有静态或线程存储持续时间的对象是零初始化的
  • 如果初始化器的数量少于数组元素,则未明确初始化的每个元素都应进行零初始化
  • 值初始化期间,如果T是没有用户提供的构造函数的(可能是cv限定的)非联合类类型,则该对象是零初始化的.

零初始化T类型的对象或引用意味着:

  • 如果T是标量类型(3.9),则将对象设置为值0(零),作为整数常量表达式,转换为T;
  • 如果T是(可能是cv限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都是零初始化的,并且填充初始化为零位;
  • 如果T是(可能是cv限定的)联合类型,则对象的第一个非静态命名数据成员初始化为零,并且填充初始化为零位;
  • 如果T是数组类型,则每个元素都是零初始化的;
  • 如果T是引用类型,则不执行初始化.

2.初始化程序类型:8.5.6 [dcl.init] _default-initialize_

在以下情况下执行

  • 如果省略new-initializer,则默认初始化该对象; 如果没有执行初始化,则该对象具有不确定的值.
  • 如果没有为对象指定初始化程序,则默认初始化该对象,但具有静态或线程存储持续时间的对象除外
  • 在构造函数初始值设定项列表中未提及基类或非静态数据成员且调用该构造函数时.

默认初始化T类型的对象意味着:

  • 如果T是(可能是cv限定的) 非POD类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的);
  • 如果T是数组类型,则每个元素都是默认初始化的;
  • 否则,不执行初始化.

注意在C++ 11之前,当没有使用初始化程序时,只有具有自动存储持续时间的非POD类类型被认为是默认初始化的.


3.初始化程序类型:8.5.7 [dcl.init] _value-initialize_

  1. 当一个对象(无名临时,命名变量,动态存储持续时间或非静态数据成员),其初始化器是一组空的括号,即()或大括号{}

对值类型T的对象进行值初始化意味着:

  • 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的) ;
  • 如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么T的每个非静态数据成员和基类组件都是值初始化的; 那么该对象是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数.
  • 如果T是数组类型,那么每个元素都是值初始化的;
  • 否则,该对象被零初始化.

总结一下

注意标准中的相关引用以粗体突出显示

  • 新的A:默认初始化(离开A :: m未初始化)
  • new A():零初始化A,因为值初始化候选者没有用户提供或删除的默认构造函数.如果T是一个(可能是cv限定的)非联合类类型而没有用户提供的构造函数,那么该对象是零初始化的,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数.
  • new B:default-initializes(离开B :: m未初始化)
  • new B():value-initializes B,它初始化所有字段; 如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T的默认构造函数
  • new C:default-initializes C,调用默认的ctor.如果T是(可能是cv限定的)类类型(第9节),则调用T的默认构造函数,此外如果省略new-initializer,则对象默认初始化
  • new C():value-initializes C,调用默认的ctor.如果T是具有用户提供的构造函数(12.1)的(可能是cv限定的)类类型(第9节),则调用T的默认构造函数.此外,初始值为空的括号集(即())的对象应进行值初始化