C++字节数组存储下子对象生命周期结束的影响分析
C++复合类型对象生命周期与存储复用的疑问
我希望深入了解对象生命周期结束与存储复用的细节。此前针对简单对象的复用问题已经得到明确结论:无论复用覆盖范围如何,都会结束基础类型或指针类型对象的生命周期。
这一问题延伸到复合类型对象时,需要进一步明确。以如下结构体为例:
struct A { double d; int i; double c1; double d1; double d2; };
此前的讨论中,若直接将A类型对象作为placement new的目标,会被视为整体复用存储,导致整个A对象及所有子对象的生命周期结束(比如子对象c1)。但如果底层存储由字节数组提供,情况会有所不同吗?
以下是测试代码:
#include <cstddef> #include <cstdint> #include <memory> struct A { double d; int i; double c1; double d1; double d2; }; int main() { alignas(A) std::byte storage[sizeof(A)]; std::construct_at(reinterpret_cast<A*>(storage), 1., 2, 3., 4., 5.); // storage中存在一个存活的A对象 std::construct_at( reinterpret_cast<std::uint16_t*>(storage + offsetof(A, c1)), std::uint16_t{0}); // c1的存储被复用,因此结束c1的生命周期:这是否会结束A对象的生命周期?是否会结束其他子对象的生命周期? }
当然这种写法没有实际用途,但能帮助明确子对象与封闭对象的生命周期关系。
核心问题
- 结束子对象c1的生命周期是否会导致封闭的A类型对象生命周期结束?
- 这一操作是否会结束A的其他子对象(如d、i、d1、d2)的生命周期?
个人预期与疑问
从标准中目前能找到的依据:
- 对象生命周期启动时,仅在特定场景(如调用构造函数、数组
new[]表达式)会同时启动其子对象的生命周期; - 对象生命周期结束时,仅在特定场景(如调用析构函数、数组
delete[])会同时结束其子对象的生命周期。
基于此我有如下预期:
- 封闭的A对象生命周期会结束(依据C++标准中关于对象生命周期结束的条款,当对象的存储被复用且不符合延续生命周期的条件时,对象生命周期结束);
- 除c1外的其他子对象仍处于生命周期内;
storage + offsetof(A, c1)不再是存活double的存储位置,因此通过std::launder访问该位置的uint16_t会导致未定义行为。
但我不确定的是:通过std::launder(reinterpret_cast<double*>(storage + offsetof(A, d1)))访问d1是否合法?
希望能通过C++标准条款的引用,明确上述场景的行为,验证或推翻我的预期。
内容的提问来源于stack exchange,提问作者Oersted
相关产品推荐
相关产品推荐

