You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在存储基类的vector/array中使用placement new创建派生类对象是否合法

这段C++代码是否符合标准?
#include <iostream>
#include <memory>
#include <vector>

struct A
{
    virtual ~A() 
    {
        std::cout << "~A()" << std::endl;
    }
};

struct B : public  A
{
    virtual ~B()
    {
        std::cout << "~B()" << std::endl;
    }
};

int main()
{
    std::vector<A> a(1);
    a[0].~A();
    :: new(std::addressof(a[0])) B();
}

这段代码不符合C++标准,属于未定义行为,核心原因如下:

  • std::vector<A> 分配的是固定存储A类型对象的连续内存,每个元素的存储空间大小为sizeof(A)。B作为A的派生类,其对象大小至少不小于sizeof(A)(即便无额外成员,也可能因对象模型细节存在差异)。在A类型的存储位置上构造B对象,若sizeof(B) > sizeof(A)会直接触发内存越界;即便大小相同,C++标准也不允许在静态类型为A的数组元素位置上构造派生类对象——数组元素类型在编译期已确定,无法通过运行时操作改变其静态类型对应的存储语义。
  • 即便手动调用~A()析构了原对象,后续用placement new构造B的操作依然违反了容器元素类型的静态约束,vector的元素必须始终是A类型对象,不能替换为派生类对象。

关于你提到的编译器优化争议:
支持编译期确定元素类型、跳过动态分派的观点是合理的——因为上述替换元素实际类型的操作本身就不符合标准,属于未定义行为,编译器有权假设数组/vector的元素类型不会被非法修改,因此可以安全进行静态类型优化,避免虚函数调用的开销。

而GCC与Clang的行为差异:
GCC针对不同长度的数组表现出不同行为:当数组长度≥3时,会通过虚表调用虚析构函数;当长度<3时,则直接调用~A()(限定析构)。Clang在该场景下的行为相对一致,通常会保留虚析构的动态分派逻辑。这种差异源于编译器各自的优化策略,但无论如何,原代码本身的合法性不受编译器行为影响,始终属于未定义行为。

内容的提问来源于stack exchange,提问作者Ng Yong Xiang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 00:32:55