#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.a和a2.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 x、double y等普通成员),默认的浅拷贝完全够用,无需自定义拷贝构造。
五、总结(核心要点)
- 为啥用:解决默认浅拷贝的问题(尤其是动态内存),保证拷贝后的对象独立;
- 怎么定义:
类名(const 类名& other) { 深拷贝逻辑 }(参数必须是const引用); - 什么时候触发:用已有对象初始化新对象、值传递参数、值返回对象;
- 核心区别:浅拷贝拷贝“地址”,深拷贝拷贝“内容+新内存”。
拷贝构造函数是C++“三大件”(构造、析构、拷贝构造)之一,尤其是涉及动态内存的类,必须自定义拷贝构造函数实现深拷贝,否则必出内存问题!

京公网安备 11010502036488号