C++标准是否保证在使用placement new后,未初始化的POD成员会保留其先前的值?
根据C++11,以下断言是否始终成立?
不是。
未初始化的数据成员具有“不确定”的值,这并不意味着基础内存保持不变。
: 创建类型为T的new表达式将按以下方式初始化该对象:
- 如果省略了new-initializer,则对象将进行默认初始化(8.5); 如果没有执行初始化,则对象具有不确定的值。
- 否则,将根据直接初始化的8.5的初始化规则解释new-initializer。
: 默认初始化类型为T的对象意味着:
- 如果T是(可能带有cv限定符的)类类型(Clause 9),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化无效);
- 如果T是数组类型,则每个元素都会进行默认初始化;
- 否则,不执行初始化。
: 当使用odr创建其类类型的对象(1.8)或在其第一次声明后显式默认化时,默认情况下被默认并未定义为删除的默认构造函数在其被odr-used(3.2)时隐式定义。隐式定义的默认构造函数执行类的初始化集,这些初始化集将由一个空的compound-statement的user-written默认构造函数(12.6.2)而不是ctor-initializer(12.6.2)执行。
: 在非委托构造函数中,如果某个给定的非静态数据成员或基类没有被mem-initializer-id指定(包括没有mem-initializer-list的情况,因为构造函数没有ctor-initializer),并且该实体不是抽象类的虚基类(10.4),则:
- 如果实体是具有brace-or-equal-initializer的非静态数据成员,则按8.5中指定的方式初始化该实体;
- 否则,如果实体是变量成员(9.5),则不执行初始化;
- 否则,对实体进行默认初始化(8.5)。
(NB. 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
(
reference)。
在这种情况下,它会吗?我不知道。我不认为我们能知道。
我们所知道的是,C++ 不禁止它,我相信这就是这个答案的结论。 :)
这个答案对于C++03也是一样的吗?
是的,只不过逻辑稍有不同:
: 创建一个类型为T的new-expression,将初始化对象如下:
- 如果省略了new-initializer:
- 如果T是(可能是cv-qualified的)非POD类类型(或其数组),则对象进行默认初始化(8.5)。如果T是const限定类型,则底层类类型必须具有用户声明的默认构造函数。
- 否则,所创建的对象具有不确定的值。如果T是const限定类型,或者(可能是cv-qualified的)包含(直接或间接地)const限定类型成员的POD类类型(或其数组),则程序是非法的;
- 如果new-initializer的形式为(),则该项进行值初始化(8.5);
- 如果new-initializer的形式为(expression-list),并且T是类类型,则调用适当的构造函数,使用expression-list作为参数(8.5);
- 如果new-initializer的形式为(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-qualified 版本的 T。否则,prvalue 的类型是 T。因此,实际上并不重要:你无论如何都不能合规地观察原始值。
alpha
与p
具有相同的地址。如果Foo
恰好是多态的,则绝对不是这种情况。 - Mysticialp->alpha
切换寄存器 - 在这种情况下,它将取一个不同的值。 - Mysticialnew (p) Foo;
看起来相当像是放置 new。 - Lightness Races in Orbit