放置新的和未初始化的POD成员
Placement new and uninitialized POD members
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);
}
C++03的答案是一样的吗?
C++标准是否保证未初始化的POD成员在新的放置后保留其以前的值?
根据C++11,以下断言是否总是得到满足?
否
未初始化的数据成员有一个不确定的值,这与底层内存被单独留下完全不同。
[C++11: 5.3.4/15]:
创建T
类型对象的新表达式将该对象初始化如下:
- 如果省略新初始化器,则对象为默认初始化的(8.5);如果不执行初始化,则对象的值不确定
- 否则,新初始值设定项将根据8.5的初始化规则进行解释,用于直接初始化
[C++11: 8.5/6]:
默认初始化T
类型的对象意味着:
- 如果
T
是(可能cv合格)类类型,则T
的默认构造函数被称为。(如果T
没有可访问的默认构造函数,则初始化不正确)- 如果
T
是数组类型,则默认初始化每个元素- 否则,不执行初始化
[C++11: 12.1/6]:
默认且未定义为已删除的默认构造函数是在odr使用(3.2)创建其类类型(1.8)的对象时,或在其第一次声明后显式默认时隐式定义的隐式定义的默认构造函数执行类的一组初始化由用户为该类编写的默认构造函数执行,该类没有ctor初始值设定项(12.6.2)和空的复合语句
[C++11: 12.6.2/8]:
在非委托构造函数中,如果给定的非静态数据成员或基类不是由mem初始值设定项id指定的(包括由于构造函数没有ctor初始值设置项而没有mem初始项列表的情况),并且实体不是抽象类的虚拟基类(10.4),则
- 如果实体是具有大括号或相等初始值设定项的非静态数据成员,则实体将按照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
,他们用它来表示"干净的内存"(参考)。
在这种情况下会吗?我不知道。我不认为我们能知道。
我们所知道的是C++并没有禁止它,我相信这会让我们得出这个答案的结论。:)
C++03的答案相同吗?
是,尽管逻辑略有不同:
[C++03: 5.3.4/15]:
创建T
类型对象的新表达式将该对象初始化如下:
- 如果省略新初始值设定项:
- 如果
T
是(可能是cv限定的)非POD类类型或其数组,则默认初始化对象(8.5)。如果T
是const限定的类型,则底层类类型应具有用户声明的默认构造函数- 否则,创建的对象具有不确定的值如果
T
是const限定类型,或者(可能cv限定)POD类类型(或其数组)包含(直接或间接)const限定型的成员,则程序格式错误- 如果新初始值设定项的形式为
()
,则该项被值初始化(8.5)- 如果新初始值设定项的形式为
(expression-list)
,并且T
是类类型,则使用expression-list
作为参数调用适当的构造函数(8.5)- 如果新初始值设定项的形式为
(expression-list)
,而T
是算术、枚举、指针或指向成员类型的指针,并且expression-list
仅包括一个表达式,则对象被初始化为表达式(8.5)的(可能转换的)值- 否则,新表达式格式错误
现在,所有这些都是我对初始化规则的严格解释。
实际上,我认为你可能正确地看到了与放置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的类型是T
的cv不合格版本。否则,prvalue的类型为T
。
所以这其实并不重要:无论如何你都无法遵守原始值。
C++11 12.6.2/8"初始化基地和成员"说:
在非委托构造函数中,如果给定的非静态数据成员或基类不是由mem初始值设定项id指定的(包括由于构造函数而没有mem初始值设定项列表的情况没有ctor初始值设定项),并且实体不是的虚拟基类一个抽象类(10.4),然后是
- 如果实体是具有大括号或相等初始值设定项的非静态数据成员,则实体将按照8.5
- 否则,如果实体是变体成员(9.5),则不执行初始化
- 否则,实体默认初始化(8.5)
int
上的默认初始化不起任何作用(8.5/6"Initializers"):
默认初始化T类型的对象意味着:
- 如果T是(可能是cv限定的)类类型(第9条),则调用T的默认构造函数(初始化为如果T没有可访问的默认构造函数,则格式错误)
- 如果T是数组类型,则默认初始化每个元素
- 否则,不执行初始化
因此成员alpha
应单独保留。
- 如何在 malloc 内存中初始化非 POD 数据
- 使用 std::index_sequence 初始化具有固定大小数组成员的 POD 结构容器
- 非 POD 的零初始化
- 如何让MSVC编译器优化多步POD初始化?
- 默认情况下初始化时 POD 是否给予特殊处理 (C++14)?
- 类POD成员变量初始化
- C++函数中的多线程静态 POD 初始化
- 初始化新对象时C++默认构造函数和 POD 问题
- POD成员默认初始化无括号
- 视觉C++似乎正在零初始化一个不应该的类的 POD 成员
- 如何在派生类中初始化继承的模板POD结构
- 构造函数初始化列表中POD类型的初始化
- 使用初始化列表的 POD 类型初始化不起作用
- 继承的 POD 类型的值初始化
- 初始化POD数组中的第一个元素,保持其余元素未初始化
- POD变量的直接初始化不起作用,但当将变量推到向量上时,复制初始化起作用
- POD 类对象初始化是否需要构造函数
- 为什么在初始化项列表中初始化 POD 数据时必须使用 C 样式类型转换
- 如何使用C++中的括号语法初始化POD数组
- 获取默认初始化(NOT value/零初始化)POD作为右值