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

如何让拷贝构造函数同步拷贝虚表?C++标准实现方案问询

问题:基类拷贝构造中原地初始化派生类虚表

现有类继承结构 I <- A、I <- B,要求基类I的拷贝构造函数能够原地初始化派生类的虚表,已知sizeof(A) == sizeof(B) == sizeof(I),优先避免动态内存分配,允许使用placement new。

原代码

#include <iostream>

using namespace std;

class B;

class I
{
public:
    virtual ~I()
    {
        cout << "~I()\n";
    }

    I()
    {
        cout << "I ctor\n";
    }

    virtual void print()
    {
        cout << "I::print()\n";
    }

    I(const I& clone);

};

class A : public I
{
public:
    ~A() override
    {
        cout << "~A()\n";
    }

    void print() override
    {
        cout << "A::print()\n";
    }
};

class B : public I
{
public:
    B()
    {
        cout << "B ctor\n";
    }

    B(const B& clone)
    {
        cout << "B copy ctor\n";
    }
    
    ~B() override
    {
        cout << "~B()\n";
    }

    void print() override
    {
        cout << "B::print()\n";
    }

};

I::I(const I& clone)
{
    cout << "B copy ctor 2\n";
    B* b = dynamic_cast<B*>((I*)&clone);
    if (b != nullptr)
    {
        this->~I();
        new (this) B(*b);
    }
}


class T
{
public:
    I GetA()
    {
        return B();
    }

};


int main()
{
    cout << "main() ->\n";
    T t;
    {
        cout << "- GetA()\n";
        I i = t.GetA();
        i.print();
        cout << "- before scope end\n";
    }
    cout << "- main scope\n";
    cout << "<- main()\n";
}

当前输出

main() ->
- GetA()
I ctor
B ctor
B copy ctor 2
~I()
I ctor
B copy ctor
~B()
~I()
I::print()
- before scope end
~I()
- main scope
<- main()

期望输出

main() ->
- GetA()
I ctor
B ctor
B copy ctor 2
~I()
I ctor
B copy ctor
~B()
~I()
B::print()            <--- changed
- before scope end
~B()                  <--- added
~I()
- main scope
<- main()

解决方案

原代码核心问题是返回值切片导致源对象丢失派生类类型信息,且依赖未定义行为的dynamic_cast判断类型。通过给基类添加虚克隆方法,可安全实现原地构造派生类对象。

修改后的代码:

#include <iostream>

using namespace std;

class I
{
public:
    virtual ~I()
    {
        cout << "~I()\n";
    }

    I()
    {
        cout << "I ctor\n";
    }

    virtual void print()
    {
        cout << "I::print()\n";
    }

    I(const I& clone);

    // 纯虚方法:将当前对象拷贝到指定内存地址
    virtual void clone_to(void* dest) const = 0;
};

class A : public I
{
public:
    ~A() override
    {
        cout << "~A()\n";
    }

    void print() override
    {
        cout << "A::print()\n";
    }

    void clone_to(void* dest) const override {
        new (dest) A(*this);
    }
};

class B : public I
{
public:
    B()
    {
        cout << "B ctor\n";
    }

    B(const B& clone)
    {
        cout << "B copy ctor\n";
    }
    
    ~B() override
    {
        cout << "~B()\n";
    }

    void print() override
    {
        cout << "B::print()\n";
    }

    void clone_to(void* dest) const override {
        new (dest) B(*this);
    }
};

I::I(const I& clone)
{
    cout << "I copy ctor\n";
    // 销毁默认构造的基类对象
    this->~I();
    // 调用派生类的clone_to,原地构造对应类型的拷贝
    clone.clone_to(this);
}

class T
{
public:
    I GetA()
    {
        return B();
    }
};

int main()
{
    cout << "main() ->\n";
    T t;
    {
        cout << "- GetA()\n";
        I i = t.GetA();
        i.print();
        cout << "- before scope end\n";
    }
    cout << "- main scope\n";
    cout << "<- main()\n";
}

修改说明

  1. 添加虚克隆方法:在基类I中定义纯虚方法clone_to,要求派生类实现该方法,完成自身对象的原地拷贝构造。
  2. 修改基类拷贝构造:先销毁默认构造的I对象,再调用源对象的clone_to方法,在当前内存位置原地构造派生类对象,确保虚表被正确初始化。
  3. 派生类实现克隆逻辑:A和B分别实现clone_to,使用placement new在指定内存地址创建自身类型的拷贝对象。

运行修改后的代码,输出将符合期望:

main() ->
- GetA()
I ctor
B ctor
I copy ctor
~I()
B copy ctor
~B()
~I()
B::print()
- before scope end
~B()
~I()
- main scope
<- main()

内容的提问来源于stack exchange,提问作者TarmoPikaro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 02:58:09