C++中的拷贝构造函数、浅拷贝与深拷贝
拷贝/复制构造函数
C++中两种初始化的区别
1 2 3 4 5 6 7 8 9
| 1.对于基本类型没有区别 拷贝初始化 int a=5; 直接初始化 int a(5) 2.对于类类型 直接初始化直接调用实参匹配的构造函数 例如: A x(2);//直接初始化,调用构造函数 拷贝初始化总是调用拷贝构造函数 A y=x;//拷贝初始化,调用拷贝构造函数,
|
基本概念
拷贝构造函数(Copy Constructor)是构造函数的一种,用于 创建新对象时,用已有对象来初始化它,如果没有定义复制构造函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能
1
| ClassName(const ClassName &other);
|
加const好处
- 防止误操作将原对象内容修改
- 防止传入const对象造成【权限放大】
使用条件
使用同类型的对象去初始化另一个对象时
1 2
| CDate date_1(2025,04,06); CDate date_2 = date_1; // 调用date_2的拷贝构造函数
|
将一个对象作为实参传递给一个非引用类型的形参
1 2 3 4 5 6
| void printDate(CDate date) { date.show(); } ... printDate(date_2); // 实参传值到形参,调用拷贝构造函数
|
从一个返冋类型为非引用类型的函数返回一个对象
1 2 3 4 5
| CDate g_date(2025,04,06); CDate getDate() { return g_date; // 返回对象时,调用拷贝构造函数 }
|
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| #include <iostream> #include <stdio.h> using namespace std; class CDate{ public: CDate(int year, int mon, int day); CDate(const CDate& date); ~CDate(); void show() { cout << "Date: " << str << endl; } private: int m_year; int m_mon; int m_day; char *str; };
CDate::CDate(int year, int mon, int day){ m_year = year; m_mon = mon; m_day = day; str = new char[64]; sprintf(str, "%4d.%02d.%02d", year,mon,day); cout << "Calling Constructor" << ", this=" << this <<endl; }
CDate::CDate(const CDate& date){ m_year = date.m_year; m_mon = date.m_mon; m_day = date.m_day; str = new char[64]; sprintf(str, "%4d.%02d.%02d", m_year,m_mon,m_day); cout << "Calling Copy Constructor" << ", this=" << this <<endl; }
CDate::~CDate(){ cout << "Calling Destructor" << ", this=" << this <<endl; delete [] str; } void printDate(CDate date){ date.show(); } CDate g_date(2025,04,06);; CDate getDate(){ return g_date; }
int main(){ CDate date_1(2025,04,05); CDate date_2 = date_1; date_1.show(); date_2.show(); cout << endl; printDate(date_2); cout << endl; getDate(); cout << endl; return 0; }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Calling Constructor, this=0x4a8030 Calling Constructor, this=0x6ffdb0 Calling Copy Constructor, this=0x6ffd90 Date: 2024.06.05 Date: 2024.06.05
Calling Copy Constructor, this=0x6ffdd0 Date: 2024.06.05 Calling Destructor, this=0x6ffdd0
Calling Copy Constructor, this=0x6ffdf0 Calling Destructor, this=0x6ffdf0
Calling Destructor, this=0x6ffd90 Calling Destructor, this=0x6ffdb0 Calling Destructor, this=0x4a8030
|
传值调用和传引用调用
在传 “值” 调用的时候, 形参是实参的拷贝,改变形参的值并不会影响外部实参的值。
在传 “引用” 调用的时候,形参是实参的别名,共同拥有一个地址,改变形参的值,就相当于对实参本身进行操作。
两者之间的区别:传 “值” 调用会比传 “引用” 调用 中间多一步拷贝的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| class Date{ public: Date(int year = 2025,int month = 4,int day = 6) { _year = 2025; _month = 4; _day = 6; } Date(const Date& d) { cout << "调用拷贝构造" << endl; _year = d._year; _month = d._month; _day = d._day; } void Print() { std::cout << "year:" << _year << std::endl; std::cout << "month:" << _year << std::endl; std::cout << "day:" << _year << std::endl; } ~Date() { cout << "调用析构构造" << endl; cout << endl; _year = 0; _month = 0; _day = 0; } private: int _year; int _month; int _day; };
void Func1(Date d1) { cout << "Func1函数的调用" << endl; }
void Func2(Date& d2) { cout << "Func2函数的调用" << endl; } int main() { Date d; Func1(d); Func2(d); return 0; }
|
输出:
1 2 3 4 5 6
| 调用拷贝构造 Func1函数的调用 调用析构构造
Func2函数的调用 调用析构构造
|
浅拷贝
浅拷贝 = 默认拷贝行为,编译器为你生成的拷贝构造函数会做成员逐个字节拷贝
大白话:只是增加一个指针指向已经存在的内存地址
结构简单、无动态内存(如int、float)
注:浅拷贝两个对象共享同一个内存空间,导致一个对象被析构时,另一个对象可能会访问已删除的内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> using namespace std; class Shallow{ public: int *data; Shallow(int value) { data = new int(value); } Shallow(const Shallow &other) { data = other.data; } ~Shallow() { delete data; } }; int main() { Shallow obj1(10); Shallow obj2 = obj1; cout << "obj1.data: " << *obj1.data << std::endl; cout << "obj2.data: " << *obj2.data << std::endl; return 0; }
|
当 obj1
或 obj2
被销毁时,data
指针所指向的内存会被删除,导致另一个对象访问已释放的内存,析构两次会造成程序的奔溃
深拷贝
深拷贝 = 自己写拷贝构造函数,分配新内存并复制内容
大白话:增加一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存
有指针、数组、动态资源时必须使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include <iostream> using namespace std; class Deep { public: int *data; Deep(int value) { data = new int(value); } Deep(const Deep &other) { data = new int(*other.data); } ~Deep() { delete data; }
}; int main() { Deep obj1(10); Deep obj2 = obj1; cout << "obj1.data: " << *obj1.data << std::endl; cout << "obj2.data: " << *obj2.data << std::endl; *obj2.data = 20; cout << "After modification:" << std::endl; cout << "obj1.data: " << *obj1.data << std::endl; cout << "obj2.data: " << *obj2.data << std::endl; return 0; }
|
输出:
1 2 3 4 5
| obj1.data: 10 obj2.data: 10 After modification: obj1.data: 10 obj2.data: 20
|
参考:
【C++】深度解析–拷贝构造函数
【C++ | 拷贝构造函数】一文了解C++的 拷贝(复制)构造函数