安置新的和未初始化的POD成员

Kri*_*ege 14 c++ initialization placement-new

C++标准是否保证未初始化的POD成员在新的位置后保留其先前的值?

或者更准确地说,根据C++ 11,下面的断言是否总能得到满足?

#include <cstdlib>
#include <cassert>

struct Foo {
    int alpha; // NOTE: Uninitialized
    int beta = 0;
};

int main()
{
    void* p = std::malloc(sizeof (Foo));
    int i = some_random_integer();
    static_cast<Foo*>(p)->alpha = i;
    new (p) Foo;
    assert(static_cast<Foo*>(p)->alpha == i);
}
Run Code Online (Sandbox Code Playgroud)

C++ 03的答案是否相同?

Lig*_*ica 20

C++标准是否保证未初始化的POD成员在新的位置后保留其先前的值?

根据C++ 11,是否总能满足以下断言?

没有.

未初始化的数据成员具有不确定的值,这与说基础内存保持不变完全相同.

[C++11: 5.3.4/15]:新表达式创建类型的对象T如下初始化该对象:

  • 如果省略new-initializer,则默认初始化对象(8.5); 如果没有执行初始化,则该对象具有不确定的值.
  • 否则,根据8.5的初始化规则解释new-initializer以进行直接初始化.

[C++11: 8.5/6]:默认初始化类型对象T意味着:

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

[C++11: 12.1/6]:使用odr-used(3.2)创建其类类型(1.8)的对象或在第一次声明后显式默认为默认构造函数时,默认构造函数是默认的并且未定义为已删除.隐式定义的默认构造函数执行该类的初始化集,该初始化集将由该用户编写的默认构造函数执行,该类没有ctor-initializer(12.6.2)和空复合语句.

[C++11: 12.6.2/8]:在非委托构造函数中,如果给定的非静态数据成员或基类未由mem-initializer-id指定(包括没有mem-initializer-list的情况,因为构造函数没有ctor-initializer)然后,实体不是抽象类(10.4)的虚基类

  • 如果实体是具有大括号或等于初始值的非静态数据成员,则按照8.5中的规定初始化该实体;
  • 否则,如果实体是变体成员(9.5),则不执行初始化;
  • 否则,实体默认初始化(8.5).

(注意:第一个选择12.6.2/8是如何beta处理您的会员)

[C++11: 8.5/6]:默认初始化类型对象T意味着:

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

[C++11: 8.5/11]:如果没有为对象指定初始化程序,则默认初始化该对象; 如果未执行初始化,则具有自动或动态存储持续时间的对象具有不确定的值.

编译器可以选择在分配期间将底层内存清零(或以其他方式更改).例如,已知调试模式下的Visual Studio会将可识别的值0xDEADBEEF写入内存以帮助调试; 在这种情况下,您可能会看到0xCDCDCDCD它们用来表示"清洁记忆"(参考).

请问它,在这种情况下?我不知道.我不认为我们可以知道.

我们知道的是C++并没有禁止它,我相信这会让我们得出这个答案的结论.:)


C++ 03的答案是否相同?

是的,虽然逻辑略有不同:

[C++03: 5.3.4/15]:新表达式创建类型的对象T如下初始化该对象:

  • 如果省略new-initializer:
    • 如果T(可能是cv限定的)非POD类类型(或其数组),则对象是默认初始化的(8.5).如果T是const限定类型,则基础类类型应具有用户声明的默认构造函数.
    • 否则,创建的对象具有不确定的值.如果T是const限定类型,或者(可能是cv限定的)POD类类型(或其数组)包含(直接或间接)const限定类型的成员,则该程序是不正确的;
  • 如果new-initializer属于表单(),则该项是值初始化的(8.5);
  • 如果new-initializer是表单类型(expression-list)并且T是类类型,则使用expression-list参数(8.5)调用相应的构造函数;
  • 如果新初始化的形式的(expression-list)T是一个算术,枚举,指针或指针到构件类型和expression-list包含正好一个表达,那么该对象被初始化为表达式(8.5)的(可能转换的)值;
  • 否则new-expression是不正确的.

现在,所有这些都是我对初始化规则的严格解释.

说实话,我认为你可能正确看到与放置operator new语法定义的潜在冲突:

[C++11: 18.6.1/3]: 备注:故意不执行任何其他操作.

下面的示例解释了放置new"对于在已知地址处构造对象可能是有用的".

但是,它实际上并没有谈到在一个已知地址构建一个对象的常见用法,而没有混淆已经存在的值,但短语"不执行其他操作"确实表明意图是你的"不确定值"是以前记忆中的任何东西.

或者,它可能只是禁止操作员自己采取任何行动,让分配器自由.它确实在我看来,标准的努力使重要的一点是,任何新的内存分配.

无论如何,访问此数据会调用未定义的行为:

[C++11: 4.1/1]:T可以将非函数非数组类型的glvalue(3.10)转换为prvalue.如果T是不完整类型,则需要进行此转换的程序格式不正确.如果glvalue引用的对象不是类型的对象,T并且不是派生类型的对象T,或者如果对象未初始化,则需要此转换的程序具有未定义的行为.如果T是非类类型,则prvalue的类型是cv-nonqualified version T.否则,prvalue的类型是T.

所以它并不重要:无论如何你无法顺从地观察原始值.


Mic*_*urr 0

C++11 12.6.2/8“初始化基数和成员”说:

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

  • 如果该实体是具有大括号或等于初始化器的非静态数据成员,则该实体按照 8.5 中的规定进行初始化;
  • 否则,如果实体是变体成员(9.5),则不执行初始化;
  • 否则,该实体将被默认初始化(8.5)。

默认初始化int不执行任何操作(8.5/6“初始化程序”):

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

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

所以该成员alpha应该独自一人。