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
// g++ 11_Copy_Constructor_Date.cpp
#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: " << m_year << "." << m_mon << "." << m_day << endl;
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; // 3、返回对象时,调用拷贝构造函数
}

int main(){
CDate date_1(2025,04,05);
CDate date_2 = date_1; // 1、调用date_2的拷贝构造函数

date_1.show();
date_2.show();
cout << endl;

printDate(date_2); // 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         //g_date的构造函数
Calling Constructor, this=0x6ffdb0 //date_1的构造函数
Calling Copy Constructor, this=0x6ffd90 //date_2的拷贝构造函数
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:
// 构造函数
// 通常都会先运行构造函数再运行Init
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); // 创建 obj1
Shallow obj2 = obj1; // 浅拷贝 obj1 到 obj2
cout << "obj1.data: " << *obj1.data << std::endl;
cout << "obj2.data: " << *obj2.data << std::endl;
// 由于 obj1 和 obj2 都指向同一内存区域,销毁一个对象时会导致问题
return 0;
}

obj1obj2 被销毁时,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; // 正常删除内存
}
/*
public:
char *data;
// 构造函数
Deep(const char *value) {
data = new char[strlen(value) + 1]; // 分配内存
strcpy(data, value); // 复制字符串
}
// 深拷贝构造函数
Deep(const Deep &other) {
// 为新对象分配新的内存空间
data = new char[strlen(other.data) + 1];
strcpy(data, other.data); // 拷贝数据
}
// 析构函数
~Deep() {
delete[] data; // 删除动态分配的内存
}
*/
};
int main() {
Deep obj1(10); // 创建 obj1
Deep obj2 = obj1; // 深拷贝 obj1 到 obj2
cout << "obj1.data: " << *obj1.data << std::endl;
cout << "obj2.data: " << *obj2.data << std::endl;
// 修改 obj2 中的数据
*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++的 拷贝(复制)构造函数


猫猫🐱



© 2025 子非鲲 使用 Stellar 创建
共发表 44 篇 Blog · 总计 109.6k 字