c++深拷贝和浅拷贝

发布于:2025-07-26 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、浅拷贝

  • 本质:简单地复制对象的成员值。如果成员里有指针,新对象和原对象的指针会指向同一块内存
    比如你有对象 A,里面指针 p 指向堆内存 0x123;用 A 拷贝出对象 BB 的指针 p 也指向 0x123

  • 问题

    • 若其中一个对象修改指针指向的内容,另一个对象也会受影响(因为共享内存)。
    • 当对象销毁时(调用析构函数),两个对象的指针会重复释放同一块内存,程序会崩溃(C++ 不允许重复释放同一块堆内存 )。

    类比:你和朋友共用一把家里的钥匙(指针),你把家里东西改了(修改内存内容),朋友看到的也会变;但如果你们先后 “销毁钥匙权限”(调用析构释放内存),第二次释放就会出问题。

二、深拷贝

  • 本质:给新对象的指针重新开一块独立的堆内存,再把原对象指针指向的内容完整复制到新内存里。
    还是用上面的例子:对象 A 指针 p 指向 0x123;拷贝出对象 B 时,B 会新申请一块内存 0x456,再把 0x123 里的内容复制到 0x456B 的指针 p 指向 0x456

  • 优点

    • 两个对象的指针各自指向独立内存,修改内容互不影响。
    • 析构时,各自释放自己的内存,不会重复释放,程序更安全。

    类比:你和朋友各自配一把钥匙(新内存),各自钥匙开各自家门(独立内存),改自己家东西不影响对方,销毁钥匙(析构)也不会冲突。

三、如何实现深拷贝?(以代码为例)

假设我们有一个简单的 STRING 类(简化版,类似你提供的代码):

cpp

运行

class STRING {
private:
    char* _str;  // 指向堆内存的指针
public:
    // 构造函数:初始化字符串
    STRING(const char* str) {
        _str = new char[strlen(str) + 1];  // 开堆内存存字符串
        strcpy(_str, str);
    }

    // 析构函数:释放堆内存
    ~STRING() {
        delete[] _str;
    }

    // ... 其他成员函数
};
1. 深拷贝的拷贝构造函数

cpp

运行

STRING(const STRING& s) {
    // 1. 给新对象的指针开独立堆内存(大小和原字符串一样,+1 存 '\0')
    _str = new char[strlen(s._str) + 1];  
    // 2. 把原对象字符串内容,复制到新内存里
    strcpy(_str, s._str);  
}

作用:创建新对象时,不共用原对象内存,而是 “另开新内存 + 复制内容”,避免浅拷贝的问题。

2. 深拷贝的赋值运算符重载

cpp

运行

STRING& operator=(const STRING& s) {
    // 防御性检查:避免自己赋值给自己(比如 a = a; 这种情况,释放内存会出问题)
    if (this != &s) {  
        // 1. 先释放当前对象旧的堆内存(防止内存泄漏)
        delete[] _str;  
        // 2. 开新内存,复制内容(和拷贝构造逻辑一样)
        _str = new char[strlen(s._str) + 1];  
        strcpy(_str, s._str);  
    }
    return *this;  // 返回当前对象,支持链式赋值(比如 a = b = c; )
}

作用:处理 “对象赋值” 场景(比如 a = b; )。需要先释放自己旧的内存,再深拷贝新内容,否则会内存泄漏(旧内存没释放,又开新内存,原内存就丢了,无法释放 )。

四、总结

  • 浅拷贝:简单复制指针值,共享内存,容易出 “重复释放” 或 “内容互相影响” 的问题。
  • 深拷贝:给新对象指针重新开内存、复制内容,让对象互相独立,解决浅拷贝的隐患。
  • 实现关键:在拷贝构造函数赋值运算符重载里,手动 “开新内存 + 复制内容”,别依赖编译器默认的浅拷贝逻辑。

理解后,写涉及指针成员(动态内存)的类时,记得补全深拷贝的这两个函数,否则程序大概率会崩溃或内存泄漏~


网站公告

今日签到

点亮在社区的每一天
去签到