深拷贝和浅拷贝

内容简介

浅拷贝是逐个成员复制;深拷贝是在复制指针指向的数据时重新申请资源。只要类中有裸指针管理堆资源,就必须认真区分这两者。

浅拷贝

默认拷贝构造通常是浅拷贝:

class Person {
public:
    Person(int age, int height)
        : age_(age), height_(new int(height))
    {
    }
 
    ~Person()
    {
        delete height_;
    }
 
private:
    int age_;
    int* height_;
};

如果这样拷贝:

Person p1(18, 160);
Person p2(p1);

默认拷贝会让 p1.height_p2.height_ 指向同一块堆内存。两个对象析构时都会 delete 同一地址,导致重复释放。

深拷贝

自己实现拷贝构造,重新申请资源:

class Person {
public:
    Person(int age, int height)
        : age_(age), height_(new int(height))
    {
    }
 
    Person(const Person& other)
        : age_(other.age_), height_(new int(*other.height_))
    {
    }
 
    Person& operator=(const Person& other)
    {
        if (this != &other) {
            int* new_height = new int(*other.height_);
            delete height_;
            height_ = new_height;
            age_ = other.age_;
        }
        return *this;
    }
 
    ~Person()
    {
        delete height_;
    }
 
private:
    int age_;
    int* height_;
};

现代 C++ 推荐写法

优先避免裸指针资源:

class Person {
public:
    Person(int age, int height)
        : age_(age), height_(height)
    {
    }
 
private:
    int age_;
    int height_;
};

如果确实需要动态资源,优先使用标准库类型:

class Buffer {
public:
    explicit Buffer(std::size_t size) : data_(size) {}
 
private:
    std::vector<std::byte> data_;
};

最佳代码实践

  • 类中有裸指针资源时,必须明确拷贝、赋值、析构、移动语义。
  • 能用 std::stringstd::vectorstd::unique_ptr 时,不要手写裸指针。
  • 独占资源类通常禁止拷贝,允许移动。
  • 共享资源时明确使用 std::shared_ptr,不要让裸指针表达共享所有权。

错误用法

class Person {
public:
    int* height = new int(160);
};
 
Person p1;
Person p2 = p1;  // 两个对象共享同一个指针,语义不清

注意事项

  • 深浅拷贝问题本质是资源所有权问题。
  • 现代 C++ 的目标不是“熟练写深拷贝”,而是尽量用 RAII 类型避免手写资源管理。
  • 相关:RAII智能指针移动语义

学习路径