关于C++标准中数组新创建子对象的指针运算及对象重写有效性的技术问询
首先咱们把问题拆解清楚:你给出的这段代码里,核心疑问是foreign_function中标记为(*)的char* pointer5 = p_object4 + 6;这个指针算术操作是否符合C++标准,进而判断后续重写object3的操作是否有效。先贴出完整代码方便参考:
#include <iostream> void foreign_function(short* s); int main() { alignas(8) char object1[16]; short* p_object2 = new(object1 + 2) short(10); // 创建short类型的object2 long* p_object3 = new(object1 + 8) long(100); // 创建long类型的object3 foreign_function(p_object2); std::cout << *p_object3 << std::endl; } void foreign_function(short* s) { char* p_object4 = new(s) char(); // 创建char类型的object4 char* pointer5 = p_object4 + 6; // (*) 核心疑问的指针算术操作 new(pointer5) long(1000); // 尝试透明替换原object3 }
接下来结合C++标准(N4861版本)的相关条款一步步分析:
一、梳理各对象的生命周期与存储关系
object1是alignas(8)修饰的char[16]数组,它的生命周期从main启动直到结束,全程未被整体销毁或重用,因此生命周期始终有效。- 用placement new创建
object2(short类型)在object1+2位置时,根据[intro.object]第3段规则,object1为object2提供了存储,因此object1的生命周期不会终止,但object1中被object2覆盖的第2、3个char元素(对应索引2、3)的生命周期会结束(存储被重用,符合[basic.life]第1.5段)。 - 同理,创建
object3(long类型)在object1+8位置时,object1中对应索引8到11的char元素生命周期结束。 - 进入
foreign_function后,在s(即object2的地址)创建object4(char类型):此时object2的存储被重用,生命周期结束;而根据[intro.object]第2段规则,object4会成为object1的子对象——因为object1生命周期仍在,object4的存储完全在object1范围内,且类型与object1的元素类型一致(均为char)。
二、核心疑问:p_object4 + 6的指针算术是否合法?
这部分的关键是判断p_object4是否可被视为指向object1数组的一个元素的指针,因为[expr.add]第4.2段明确要求:只有当原始指针指向数组对象的某个元素时,指针算术的结果才能指向该数组的其他元素(或超出末尾一个位置),否则行为未定义。
根据[intro.object]第2段的规则,object4是object1的子对象,且类型与object1的元素类型完全一致,创建位置正好是object1原来的第2个元素(索引2)的位置。此时p_object4本质上就是指向object1数组索引2处元素的指针——虽然该位置原来的char元素生命周期已结束,但新创建的object4作为object1的子对象,完全承接了这个数组元素的“身份”。
p_object4 + 6的计算结果指向object1数组索引2+6=8处的元素,这个位置在object1的16个元素范围内(索引0-15),完全符合[expr.add]第4.2段的要求,因此这个指针算术操作是合法的。
三、重写object3的操作是否有效?
当用pointer5(指向object1+8)通过placement new创建新的long对象时:
- 原
object3的存储被重用,其生命周期根据[basic.life]第1.5段终止。 - 新创建的
long对象与原object3类型完全一致,存储完全重叠,且原object3类型非const、也不包含const成员或引用成员,符合[basic.life]第8段的“透明替换”规则。因此后续std::cout << *p_object3的操作是合法的,会输出新对象的值1000。
总结:你代码中的指针算术操作合法,重写object3的操作也完全符合当前C++标准的要求。
备注:内容来源于stack exchange,提问作者hogeegoh

