(5.3.4) new-expression:
由新表达式创建的实体具有动态存储持续时间 (3.7.4)。注意:这样一个实体的生存期不一定局限于创建的作用域。-尾注
我认为下面有一个具有自动存储持续时间的主对象和3个具有动态存储持续时间的虚拟类。
struct dummy
{
int a;
};
char local_object[256];
dummy * a = new(&local_object) dummy;
dummy * b = new(&local_object +100) dummy;
dummy * c = new(&local_object +200) dummy;用户@M.M.认为只有一个对象(local_object),其余的只是指针。这是正确的吗?
(3.7) 动态存储持续时间与用运算符新建创建的对象相关联。
发布于 2016-02-14 18:32:55
在我看来,标准(如OP中所引用的)只能在读取时解释,即操作符new创建动态存储持续时间为的对象,即使底层内存是为自动持续时间的对象获得的,也是如此。
标准在第3.8节basic.life第8段中预见到了这一确切情况,并提到了以下未定义行为的例子:
class T { };
struct B {
~B();
};
void h() {
B b;
new (&b) T;
}该段内容如下:
如果程序以静态(3.7.1)、线程(3.7.2)或自动(3.7.3)存储持续时间结束T类型对象的生存期,如果T具有一个非平凡的析构函数,则程序必须确保原始类型的对象在发生隐式析构函数调用时占据相同的存储位置;否则程序的行为是未定义的。
在本例中,程序通过重用对象b的存储来“结束”它的生命周期,正如同一节第4段所提供的那样:(重点是后加的)。
程序可以通过重用对象占用的存储或显式调用具有非平凡析构函数的类类型对象的析构函数来结束任何对象的生存期。
在示例代码中,没有调用b的析构函数,但这是可以接受的,因为第4段明确允许不调用一个非平凡的析构函数:
对于具有非平凡析构函数的类类型的对象,在对象占用的存储被重用或释放之前,程序不需要显式调用析构函数;
只要程序准备好了,就能忍受不调用析构函数的后果。
但回到第8段,b的生存期已经结束,存储已被重用以创建T类型的对象。该对象具有动态存储时间,这意味着它的析构函数不会被隐式调用。如上所述,只要程序不需要任何可能由析构函数执行的副作用,也不必显式调用析构函数。
尽管b的生存期已经结束,但b具有自动存储持续时间的事实意味着,当控制流离开其作用域时,将隐式调用其析构函数。对生存期已结束的对象调用析构函数是禁止使用生存期已结束的对象的具体情况,如第3.8节第6段所述,该段禁止调用生存期已结束但存储尚未被重用或释放的对象的非静态成员成员函数。
因此,示例程序的行为是未定义的。
但本节第7段为程序提供了一种机制,通过在同一位置重新创建相同类型的不同对象来避免未定义的行为:
如果在对象的生存期结束后,在对象占用的存储被重用或释放之前,在原始对象占用的存储位置创建一个新对象,指向原始对象的指针,引用原始对象的引用,或原始对象的名称,则可以自动引用新对象,并且一旦新对象的生存期启动,就可以使用它来操作新对象,如果: (7.1) -新物体的储存正好覆盖原来物体所占的储存位置,及 (7.2) -新对象与原始对象的类型相同(忽略顶级cv-限定符),以及 (7.3) -原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为const限定或引用类型的非静态数据成员,以及 (7.4) -原始对象是T型最派生的对象(1.8),而新对象是T型派生最多的对象(也就是说,它们不是基类子对象)。
因此,在我的解释中,下面的片段将定义行为:
class T { };
struct B {
~B();
};
void h() {
B b;
new (&b) T;
new (&b) B; /* recreate a B so that it can be destructed */
}简而言之,该标准考虑了使用分配给自动存储持续时间的对象的内存来创建具有动态存储持续时间的对象的可能性,并为执行此操作的定义良好的程序提供了一组限制和要求,从而避免了对已通过重用其存储结束其生存期的对象执行隐式析构函数的后果。
https://stackoverflow.com/questions/35389789
复制相似问题