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

C++核心指南C.67下类层次设计:如何合理支持拷贝与移动操作?

基类拷贝禁用与移动操作的务实设计方案

咱们先把C核心指南C.67的核心逻辑捋明白:基类要禁止直接的拷贝操作,因为多态场景下拷贝基类对象会触发切片问题——只能拷贝基类部分,派生类的成员会丢失,所以正确的多态拷贝应该用virtual clone()方法来实现。但这里有个坑:如果直接把基类的拷贝构造/赋值设为删除态,C会自动隐式禁用基类和所有派生类的移动操作,可移动操作对性能提升很重要,这就得找个务实的平衡方案。

一、核心设计思路

  • 基类层面:严格禁止值语义的拷贝(删除拷贝构造和拷贝赋值),但显式启用移动操作(默认化移动构造和赋值)——移动操作不会导致切片,而且能高效转移资源。
  • 提供virtual clone()纯虚方法,让派生类重写,以此实现安全的多态拷贝。
  • 派生类层面:实现自己的拷贝构造/赋值(因为基类拷贝被删,派生类默认拷贝会被禁用),同时默认化移动操作,确保自身成员能正确拷贝/移动。

二、修改后的类A和B实现

#include <string>
#include <vector>

class A {
public:
    // 1. 删除拷贝构造和赋值,彻底禁止值语义拷贝
    A(const A&) = delete;
    A& operator=(const A&) = delete;

    // 2. 显式默认移动构造和赋值,保留移动能力
    A(A&&) = default;
    A& operator=(A&&) = default;

    // 3. 多态拷贝的核心:纯虚clone方法
    virtual A* clone() const = 0;

    // 可用的构造函数(原代码把构造删了,根本没法创建对象,必须补上)
    explicit A(std::string as) : s(std::move(as)) {}

    virtual void foo() = 0;
    // 虚析构必须有,保证派生类能正确析构
    virtual ~A() = default;

protected:
    // 给派生类提供访问基类成员的接口,方便派生类实现拷贝
    const std::string& get_s() const { return s; }
    void set_s(std::string new_s) { s = std::move(new_s); }

private:
    std::string s;
};

class B : public A {
public:
    // 派生类构造:调用基类构造初始化基类成员
    explicit B(std::string as, std::vector<std::string> v)
        : A(std::move(as)), vs(std::move(v)) {}

    // 重写clone方法,返回派生类指针(协变返回类型)
    B* clone() const override {
        return new B(*this); // 这里用派生类自己的拷贝构造,安全无切片
    }

    // 手动实现拷贝构造:基类拷贝被删,默认拷贝构造会被禁用
    B(const B& other) : A(other.get_s()), vs(other.vs) {}

    // 手动实现拷贝赋值
    B& operator=(const B& other) {
        if (this != &other) {
            set_s(other.get_s()); // 用基类的protected接口更新基类成员
            vs = other.vs;
        }
        return *this;
    }

    // 默认化移动构造和赋值,利用编译器生成的高效实现
    B(B&&) = default;
    B& operator=(B&&) = default;

    void foo() override {}
    ~B() override = default;

private:
    std::vector<std::string> vs;
};

三、关键细节解析

  • 显式启用基类移动:C++的规则是,如果类有删除的拷贝操作,移动操作会被隐式删除,所以必须显式写= default来保留移动能力,这样派生类才能继承并使用移动操作。
  • clone方法的协变返回:派生类的clone可以返回B*而不是A*,这是C++允许的协变返回类型,能让用户在拿到克隆对象时直接使用派生类的接口,不用额外转换。
  • 派生类拷贝的实现:因为基类的拷贝被删除,派生类的默认拷贝构造/赋值会被禁用,所以必须手动实现,通过基类提供的protected接口来访问基类成员,完成完整的拷贝。
  • 构造函数的修正:原代码中A的构造被设为删除态,这会导致根本无法创建A或B的对象,所以必须提供可用的构造函数,同时用std::move来优化字符串的传递,避免不必要的拷贝。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:39:40