C++中通过基类重新构造对象的行为是否符合标准明确定义?
代码行为分析结论
你提供的代码包含未定义行为,仅动态内存分配场景的行为是明确定义的,各场景具体分析如下:
规则背景
你提到的C++标准6.7.3第9款的规定准确,该条款约束的是自动存储期对象(栈对象)隐式调用析构函数的场景:
当隐式析构函数调用发生时,若原类型的另一个对象并未占用该存储位置,则程序行为是未定义的
各场景逐一分析
1. 未调用method的右值引用场景
{ type&& obj = method_called{}; std::cout << "\ndestr~" << __FUNCTION__ << "\n" << std::endl; }
该场景行为明确定义:右值引用延长了临时method_called对象的生命周期,作用域结束时会正确调用派生类method_called的析构函数,无任何问题。
2. 栈临时对象调用method的场景
{ type&& obj = method_called{}; obj.method(); std::cout << "\ndestr~" << __FUNCTION__ << "\n" << std::endl; }
该场景触发未定义行为:
- 调用
obj.method()时,因为析构函数是虚函数,this->~type()实际会调用完整的method_called::~method_called(),将原派生类对象完全析构 - 后续placement new操作在原存储位置构造了一个基类
type对象,和原临时对象的method_called类型不一致 - 作用域结束触发隐式析构时,编译器默认该位置还是
method_called类型的对象,会尝试调用method_called的析构函数,此时存储位置实际是type对象,完全符合6.7.3第9款描述的未定义行为场景,输出结果不可靠。
3. 动态内存分配场景
method_called* pobj = new method_called{}; pobj->method(); std::cout << "\ndestr~" << __FUNCTION__ << "\n" << std::endl; delete pobj;
该场景行为明确定义:
- 6.7.3第9款仅约束自动存储期对象的隐式析构,动态分配的对象不存在自动触发析构的逻辑,不受该条款约束
- 调用
method完成析构和重构造后,pobj指向的存储位置是一个合法的type对象,type和method_called的析构函数都是虚函数,delete pobj时会调用当前存储对象的实际析构函数type::~type(),流程完全符合标准规定。
补充说明
你在method内先调用析构函数、再用placement new原地构造对象的操作本身是合法的,只要保证后续对该内存的使用符合对象生命周期规则即可,仅栈对象场景因为隐式析构的类型不匹配才会出现未定义行为。
内容的提问来源于stack exchange,提问作者AnArrayOfFunctions
相关产品推荐
相关产品推荐

