#include <iostream>
#include <cstring>
#pragma warning(disable : 4996)
using namespace std;

class Person {

    public:
        char* name; // 姓名
        int age;    // 年龄

        Person(const char* name, int age) {
            this->name = new char[strlen(name) + 1];
            strcpy(this->name, name);
            this->age = age;
        }

        // write your code here......
        Person(const Person& other){
            this->name = new char [strlen(other.name) + 1];
            strcpy(this->name, other.name);
            this->age = other.age;
        }

        void showPerson() {
            cout << name << " " << age << endl;
        }

        ~Person() {
            if (name != nullptr) {
                delete[] name;
                name = nullptr;
            }
        }

};

int main() {

    char name[100] = { 0 };
    int age;

    cin >> name;
    cin >> age;

    Person p1(name, age);
    Person p2 = p1;

    p2.showPerson();

    return 0;
}

一、先搞懂:为啥需要拷贝构造函数?(核心:解决“浅拷贝”的坑)

拷贝构造函数是C++类的特殊构造函数,核心作用是:👉 用一个已存在的同类型对象,创建/初始化一个新对象,并保证新对象和原对象“独立无关联”(尤其是有动态内存的类)。

先看“没有自定义拷贝构造函数”的坑(浅拷贝问题)

比如你之前写的Array类(有动态数组int* a),编译器会自动生成一个「默认拷贝构造函数」,但它做的是浅拷贝(只拷贝指针地址,不拷贝指针指向的内存):

#include <iostream>
using namespace std;

class Array {
private:
    int n;
    int* a;
public:
    Array() { // 构造函数:动态分配内存
        n = 3;
        a = new int[3]{1,2,3};
    }
    ~Array() { // 析构函数:释放内存
        delete[] a;
    }
    void show() {
        for(int i=0; i<n; i++) cout << a[i] << " ";
    }
};

int main() {
    Array a1; // 创建a1,a1.a指向内存地址0x123
    Array a2 = a1; // 用a1拷贝初始化a2(触发默认拷贝构造)
    
    a1.show(); // 输出1 2 3
    a2.show(); // 输出1 2 3(看似正常)
    
    return 0; // 程序结束时崩溃!
}

崩溃原因:

  • 浅拷贝后,a1.aa2.a指向同一块内存(0x123);
  • 程序结束时,先析构a2:释放0x123的内存;
  • 再析构a1:再次释放0x123的内存(重复释放内存,触发崩溃)。

这就是默认拷贝构造函数的致命问题:多个对象共享同一块动态内存,析构时重复释放,或修改一个对象会影响另一个

拷贝构造函数的核心价值:

自定义拷贝构造函数实现「深拷贝」—— 不仅拷贝指针,还拷贝指针指向的内存内容,让新对象和原对象拥有独立的内存,避免上述问题。

二、拷贝构造函数的基础语法(必须记死)

1. 定义格式(唯一且固定)

类名(const 类名& 源对象) {
    // 深拷贝逻辑:复制源对象的所有成员,尤其是动态内存
}

关键要点(缺一不可):

  • 函数名和类名完全相同(构造函数的特性);
  • 无返回值;
  • 参数必须是「同类型的const引用」: ✅ const:保证不修改源对象(只读); ✅ 引用:避免触发“拷贝构造函数的无限递归”(如果传值,会先拷贝参数,又触发拷贝构造);
  • 不能重载(参数类型固定,只有这一种写法)。

2. 对比:默认拷贝 vs 自定义深拷贝

Array类加自定义拷贝构造函数,解决浅拷贝问题:

class Array {
private:
    int n;
    int* a;
public:
    Array() { // 普通构造函数
        n = 3;
        a = new int[3]{1,2,3};
    }
    
    // 自定义拷贝构造函数(深拷贝)
    Array(const Array& other) {
        // 1. 拷贝普通成员
        this->n = other.n;
        // 2. 深拷贝动态内存:先分配新内存,再拷贝内容
        this->a = new int[other.n];
        for(int i=0; i<other.n; i++) {
            this->a[i] = other.a[i]; // 拷贝数组内容,而非指针地址
        }
    }
    
    ~Array() {
        delete[] a;
    }
    
    void show() {
        for(int i=0; i<n; i++) cout << a[i] << " ";
    }
    
    // 测试:修改数组内容
    void set(int idx, int val) {
        a[idx] = val;
    }
};

三、拷贝构造函数的触发场景(3种核心场景)

拷贝构造函数不是手动调用的,而是在“用已有对象创建新对象”时自动触发,常见场景有3种:

场景1:用一个对象初始化新对象(直接初始化/拷贝初始化)

int main() {
    Array a1; // 普通构造
    Array a2 = a1; // 拷贝初始化(触发拷贝构造)
    Array a3(a1);  // 直接初始化(等价写法,也触发)
    
    // 修改a1的内容,a2不受影响(深拷贝的效果)
    a1.set(0, 100);
    a1.show(); // 输出100 2 3
    a2.show(); // 输出1 2 3
    return 0; // 析构时正常释放,不崩溃
}

场景2:函数参数按「值传递」传入对象

// 函数参数是值传递(会拷贝一个临时对象)
void func(Array a) { 
    a.show();
}

int main() {
    Array a1;
    func(a1); // 传入a1时,触发拷贝构造,创建临时对象a
    return 0;
}

⚠️ 优化建议:函数参数尽量用const 类名&(引用传递),避免不必要的拷贝,提升效率。

场景3:函数按「值返回」对象

Array createArray() {
    Array a; // 局部对象
    return a; // 返回值时,触发拷贝构造,创建临时对象返回
}

int main() {
    Array a2 = createArray(); // 触发拷贝构造(C++17后可能优化,但逻辑上仍需拷贝构造)
    return 0;
}

四、关键补充:浅拷贝 vs 深拷贝(核心对比)

浅拷贝

只拷贝指针/变量的值,不拷贝动态内存

无动态内存的简单类(如Point)

重复释放、修改互相影响

深拷贝

拷贝指针+重新分配内存+拷贝内容

有动态内存的类(如Array、String)

无,对象完全独立

什么时候可以用默认拷贝构造?

如果类中没有动态内存(比如只有int xdouble y等普通成员),默认的浅拷贝完全够用,无需自定义拷贝构造。

五、总结(核心要点)

  1. 为啥用:解决默认浅拷贝的问题(尤其是动态内存),保证拷贝后的对象独立;
  2. 怎么定义类名(const 类名& other) { 深拷贝逻辑 }(参数必须是const引用);
  3. 什么时候触发:用已有对象初始化新对象、值传递参数、值返回对象;
  4. 核心区别:浅拷贝拷贝“地址”,深拷贝拷贝“内容+新内存”。

拷贝构造函数是C++“三大件”(构造、析构、拷贝构造)之一,尤其是涉及动态内存的类,必须自定义拷贝构造函数实现深拷贝,否则必出内存问题!