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

新手上路,请多包涵

我对值和默认值和零初始化感到非常困惑。尤其是当他们加入不同的标准 C++03C++11 (和 C++14 )时。

我在这里引用并尝试扩展一个非常好的答案 Value-/Default-/Zero-Init C++98C++03 以使其更通用,因为如果有人可以帮助填写需要空白来很好地了解什么时候会发生什么?

简而言之,通过示例的完整见解:

有时 new 运算符返回的内存将被初始化,有时它不会取决于您要更新的类型是 POD (plain old data) 还是包含 POD 成员的类并且正在使用编译器生成的默认构造函数。

  • C++1998 中有两种类型的初始化: 初始化和 默认初始化
  • C++2003 中,添加了第三种初始化类型,即 _值初始化_。
  • C++2011/C++2014 中,仅添加了 _列表初始化_,并且 值/默认值/零初始化 的规则发生了一些变化。

认为:

 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 */

在 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 - 默认初始化(离开 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 默认初始化 A ,编译器生成。 ctor,(离开 A::m 未初始化)( A 是 POD)

  • new A() - value-initializes A ,这是自 2. point in [dcl.init]/8 以来的零初始化

  • new B - 默认初始化 B ,编译器生成。 ctor,(离开 B::m 未初始化)( B 是非 POD)

  • new B() - value-initializes B 它对所有字段进行零初始化,因为它的默认 ctor 是编译器生成的,而不是用户定义的。

  • new C - 默认初始化 C ,它调用默认的ctor。 ( C::m 是零初始化, C 是非 POD)

  • new C() - value-initializes C ,它调用默认的ctor。 ( C::m 是零初始化的)

  • new D - default-initializes D ( D::m is uninitialized, D is non-POD)

  • new D() - value-initializes D ,它调用默认的ctor( D::m 未初始化)

  • new E - 默认初始化 E ,它调用 comp.将军医生。 ( E::m 未初始化,E是非POD)

  • new E() - value-initializes E ,零初始化 E 因为 2 点在 [dcl.init]/8 )

  • new F - 默认初始化 F ,它调用 comp.将军医生。 ( F::m 未初始化, F 非POD)

  • new F() - value-initializes F , which default-initializes F since 1. point in [dcl.init]/8 ( F ctor 函数是用户提供的,如果它是用户声明的并且在其第一次声明时没有显式默认或删除。 链接

原文由 Gabriel 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 471
1 个回答

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

创建类型为 T 的对象的 new 表达式 按如下方式初始化该对象:

  • 如果省略了 new-initializer ,则该对象是 默认初始化的(8.5)。 [ 注意: 如果不进行初始化,则该对象具有不确定的值。 ——尾注]
  • 否则, new-initializer 将根据 8.5 中的 直接 初始化规则进行解释。

默认初始化在 [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 类型的对象进行 值初始化 意味着:

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

因此 new A() 将零初始化 m 。这应该等同于 AB

  • new Cnew C() 将再次默认初始化对象,因为最后引用的第一个要点适用(C 具有用户提供的默认构造函数!)。但是,很明显,现在 m 在这两种情况下都在构造函数中进行了初始化。

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

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

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

原文由 Columbo 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题