💓博主CSDN主页:杭电码农-NEO💓
⏩专栏分类:C++初阶之路⏪
🚚代码仓库:NEO的学习日记🚚
🌹关注我🫵带你学习C++
🔝🔝
1. 前言
本章重点:
本篇文章将详细讲解拷贝构造函数
和运算符重载,并介绍const成员的概念
拷贝构造函数和运算符重载
是类和对象中六大默认成员函数
的其中两个
拷贝构造类似于构造一个一模一样的你
2. 拷贝构造函数
我们在写代码的时候会遇见这种场景:
class Date { public: Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; int main() { Date d1(2023,7,30); Date d2(d1);//用d1初始化d2 return 0; }
使用一个已经存在的对象初始化
一个正在定义的对象就是在拷贝构造!
拷贝构造函数特征:
- 拷贝构造是构造函数的一个重载形式
- 拷贝构造函数只能有一个参数且必须
是类类型对象的引用!
比如Date类的拷贝构造:
Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day; }
2.1 对拷贝构造函数参数的思考
为什么拷贝构造函数的参数
一定要是:类类型对象的引用?
因为拷贝构造是用另一个对象初始化
所以是类类型对象可以理解
但是为啥必须传引用?
这是因为不传引用会引起无限递归!
可以这样理解无限递归:
假如加上了引用,调用拷贝构造时
传对象的别名进函数,就不会有
形参拷贝一份实参的过程,就不会死递归!
所以拷贝构造函数一定要加上引用!
2.2 默认拷贝构造函数
如果用户不显示写拷贝构造函数
编译器会自动生成一个默认构造函数
默认构造函数会对内置类型完成:
值拷贝(浅拷贝)默认构造函数会去调用自定义
类型的构造函数完成拷贝
对于内置类型来说,浅拷贝可能会有问题!
举个例子:
class Stack { public: Stack(size_t capacity = 10)//构造函数 { _array = (DataType*)malloc(capacity * sizeof(DataType)); if (nullptr == _array) { perror("malloc申请空间失败"); return; } _size = 0; _capacity = capacity; } void Push(const int& data)//插入函数 { _array[_size] = data; _size++; } ~Stack()//析构函数 { if (_array) { free(_array); _array = nullptr; _capacity = 0; _size = 0; } } private: int *_array; size_t _size; size_t _capacity; }; int main() { Stack s1(10); s1.push(1); s1.push(2); s3.push(3); s3.push(4); s3.push(5); Stack s2(s1); retrn 0; }
stack类没有显示写拷贝构造函数
编译器会自动生成默认拷贝构造函数
默认构造函数是按照字节方式直接拷贝的
所以对象s1和s2的关系图如下:
array指向的空间是动态开辟的
是在堆区存储的,使用值拷贝的方式
s1和s2中的array指向的空间相同!
这么做会有啥问题?
当s1和s2生命周期结束时
会分别调用它们各自的析构函数
然而两个对象中的指针指向的空间相同
析构函数会调用两个free释放空间!
同一份空间释放两个就会出错!
2.3 对拷贝构造函数的总结
- 拷贝构造函数的参数必须是引用
- 若类中没有涉及到空间申请
则默认拷贝构造就够用了 - 下面这两种写法都是在拷贝构造:
Date d1(2023,7,30); Date d2(d1); Date d3 = d1;
拷贝构造的典型应用场景:
- 使用已存在对象创建新对象
- 函数参数类型为类类型对象
- 函数返回值类型为类类型对象