cpp_shallow_deep_copy - ShenYj/ShenYj.github.io GitHub Wiki

浅拷贝、深拷贝

浅拷贝

编译器默认的提供的拷贝是浅拷贝(shallow copy)

  • 将一个对象中所有成员变量的值拷贝到另一个对象

  • 如果某个成员变量是个指针,只会拷贝指针中存储的地址值,并不会拷贝指针指向的内存空间

  • 可能会导致堆空间多次free的问题

    #include <iostream>
    using namespace std;
    
    class Car {
        int m_price;
        char *m_name;
    public:
        /// 加上 const,在调用传参的时候,就可以直接传常量字符串了: new Car(100, “bmw”);
        /// 做为函数参数时(同样适用于const指针),const 可以接收 const 和非 const 实参
        /// 
        /// 当前示例: `char name[] = { 'b', 'm', 'w', '\0' };` 在函数栈空间, 
        /// `Car *car = new Car(100, name);` 代表着 m_name存储的是 name数组的地址,这样会造成堆空间指向栈空间,是很危险的,会出现野指针
        /// 所以安全起见,保证堆空间使用堆空间内存,申请新的堆空间内存,将 name的内容copy到堆空间来使用
        Car(int price = 0, const char *name = NULL) :m_price(price) { 
            
            if (name == NULL) return;
            /// 申请新的堆空间
            /// 1. { } 保证初始化
            /// 2. +1 是为了保证 \0 结尾标记
            ///   ps: c语言的字符串长度比实际长度大1, 有个 \0 的结束标志
            m_name = new char[strlen(name) + 1]{ };
            /// 拷贝字符串数据到新的堆空间
            strcpy(m_name, name);
        }
    
        ~Car() {
            if (m_name == NULL) return;
            delete[] m_name;
            m_name = NULL:
        }
    
        void display() {
            cout << "price is " << m_price << ", name is " << m_name << endl;
        }
    };
    
    int main() {
    
        /// 如果不做copy处理,会存在堆空间指向了栈空间 -> m_name 指向 name
        char name[] = { 'b', 'm', 'w', '\0' };
        Car *car = new Car(100, name);
    
        /// 不会调用拷贝构造函数
        /// car 和 car1 都指向相同的堆空间数据 "bwm": 只会拷贝指针中存储的地址值,并不会拷贝指针指向的内存空间
        /// 会导致堆空间多次free的问题
        /// 默认编译器提供的拷贝是浅拷贝,当存在指针时,又希望数据是独立的,就需要使用深拷贝来保证内存、数据安全
        Car car1 = car;
    
        getchar();
        return 0;
    }

深拷贝

内容拷贝

如果需要实现深拷贝(deep copy),就需要自定义拷贝构造函数

  • 将指针类型的成员变量所指向的内存空间,拷贝到新的内存空间

    class Car {
        int m_price;
        char *m_name;
    
        /// 深拷贝
        void copy(const char *name = NULL) {
            if (name == NULL) return;
            /// 申请新的堆空间
            /// { } 保证初始化,同时为了保证 char[] 以 0结尾
            m_name = new char[strlen(name) + 1]{ };
            /// 拷贝字符串数据到新的堆空间
            strcpy(m_name, name);
        }
    public:
        Car(int price = 0, const char *name = NULL) :m_price(price) { 
            copy(name);
        }
        Car(const Car &car) :m_price(car.price) { 
            copy(car.m_name);
        }
    
        ~Car() {
            if (m_name == NULL) return;
            delete[] m_name;
            m_name = NULL:
        }
    
        void display() {
            cout << "price is " << m_price << ", name is " << m_name << endl;
        }
    };
    
    int main() {
    
        Car car1(100, "bmw");
        /// 利用拷贝构造函数实现深拷贝
        Car car2 = car1;
    
        return 0;
    }
⚠️ **GitHub.com Fallback** ⚠️