万字长文帮你搞懂类的四大常用默认函数,另外两个将在另一个文章中推出!
在c++中,有四个常用的默认函数以及两个不常用的。
本文主要介绍这四种:
#构造函数
#析构函数
#拷贝构造
#赋值重载
注意,文章中的3.4.3写的是一个容易犯的陷阱,后续通过此文章复习时,要着重关注!
1.构造函数
1.1函数名与类名相同
1.1.1构造函数不需要返回值
1.1.2构造函数支持重载,也支持缺省参数
1.2构造函数的实现
以下为定义构造函数的例子:
class Time {
public:
Time() {//构造函数
_hour = 9;
_min = 17;
_sec = 20;
cout << "Time初始化" << endl;
}
Time(int hour,int min,int sec){//对构造函数的重载
_hour=hour;
_min=min;
_sec=sec;
}
void Print() {
cout << _hour << ":" << _min << ":" << _sec << endl;
}
private:
int _hour;
int _min;
int _sec;
};
1.3构造函数会在类创建的时候自动调用
我们实现对Time类的调用:
int main() {
Time t;创建一个Time类,类名为t
return 0;
}
运行结果:
Time初始化
为什么我什么都没做,依然会有打印值?
这就引出来了类的一大特性:构造函数会在类创建的时候自动调用
1.4编译器默认生成构造函数
假设我们在写类时,没有自己写构造函数,那会怎么样?
首先声明一点,每个类中都会包含构造函数与析构函数,如果我们没有写构造、析构函数,编译器会自动给我们生成一个析构函数,这个析构函数成为默认构造函数, 那这个默认构造函数有什么用呢?
先来考虑构造函数的特点:在定义class类时,自动调用,那我们是不是可以把构造函数作为对类的初始化?
要理解这一点,还需要理清楚一个定义区分:
1.4.1 内置类型成员以及自定义类型成员。
先来区分一下内置类型成员以及自定义类型成员。
什么是内置类型?
答:c++中自带的数据类型,比如int 、double、float等
什么是自定义类型?
答:用户自定义的类型数据,比如struct 、class 、enum等。
1.5默认构造函数的作用以及注意事项:
1.5.1 默认构造函数不会对内置成员初始化
我们将上面的代码稍作修改,即可:
class Time {
public:
Time(){
}
void Print() {
cout << _hour << ":" << _min << ":" << _sec << endl;
}
private:
int _hour;
int _min;
int _sec;
};
运行结果:
-858993460:-858993460:-858993460
1.5.2 构造函数会自动调用自定义类型的默认构造函数。(有待修正)
这句话读起来很别扭吧,我当初看到这句话,也很别扭。
默认构造函数:无参构造函数、全缺省构造函数以及编译器自己生成的构造函数都为默认构造函数。从这一点也可以看出来不能通过指定参数去初始化自定义类型的成员。
这里用代码解释一下:需要引入另一个class类,用来模拟自定义类型
class Time {
public:
Time(){
_hour=10;
_min=32;
_sec=50;
cout<<"调用了Time的构造函数"<<endl;
}
void Print() {
cout << _hour << ":" << _min << ":" << _sec << endl;
}
private:
int _hour;
int _min;
int _sec;
};
class Data{
public :
void Print(){
_time.Print();
}
private:
int _year;
int _month;
int _day;
Time _time;//自定义类型
};
int main(){
Data d;
d.Print();
return 0;
}
运行结果
调用了Time的构造函数
10:32:50
我们再来看这一句话
构造函数自动会调用自定义类型的默认构造函数。
我们在Data中定义了一个自动类型Time
然后在Time里面实现了Time类的构造函数
我们通过定义Data类,Data类中的构造函数就会调用自定义类型Time中的构造函数,确保自定义类型能够初始化。如果我们的自定义类型中也不存在构造函数,那么会初始化为随机值。
在c++ 11中新增一条说明:将内置函数默认初始化为0。但今天2023/10/2。使用的编译器为vs 2022 17.4.4版本中仍然为随机值。
class Time {
public:
void Print() {
cout << _hour << ":" << _min << ":" << _sec << endl;
}
private:
int _hour;
int _min;
int _sec;
};
class Data {
public:
void Print() {
_time.Print();
}
private:
int _year;
int _month;
int _day;
Time _time;
};
int main() {
Data d;
d.Print();
return 0;
}
打印出来的为随机值。
针对默认构造函数这一点:补充一下,如果类里面已经自己实现了一个带参数的构造函数(不满足默认构造函数),当我们再进行初始化时,会发生什么?
class Time {
public:
Time(int hour) {//我们自己实现一个构造函数
_hour = hour;
}
void Print() {
cout << _hour << ":" << _min << ":" << _sec << endl;
}
private:
int _hour;
int _min;
int _sec;
};
class Data {
public:
void Print() {
_time.Print();
}
private:
int _year;
int _month;
int _day;
Time _time;
};
int main() {
Data d;
d.Print();
return 0;
}
编译不通过,无法找到Time的默认构造函数
报错:error C2280: “Data::Data(void)”: 尝试引用已删除的函数
message : 编译器已在此处生成“Data::Data”
message : “Data::Data(void)”: 由于 数据成员“Data::_time”不具备相应的 默认构造函数 或重载解决不明确,因此已隐式删除函数
message : 参见“Data::_time”的声明
修改:在Time类里面添加一个默认构造函数即可。
所以建议 --每个类中都写一个默认构造函数。
2.析构函数
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
析构函数的大多数特性与构造函数相似
2.1几个不同点:
2.1.1一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载。
2.1.2析构函数与构造函数相反,是在类的生命周期结束的时候才会调用。
2.1.3无参数无返回值类型。
2.2构造函数的实现:
~ 类型名 (){
}
关注 生命周期是何时结束的。
class Data {
public:
Data(int size=4) {
arr = nullptr;
cout << "arr开辟前的地址为:" << arr << endl;
arr = (int*)malloc(sizeof(int)*size);
cout << "1.构造函数,给arr,开辟空间" << endl;
}
void Print() {
cout << "开辟后空间为:" << arr << endl;
}
~Data()
{
cout << "2.析构函数,释放arr的空间" << endl;
free(arr);
arr = nullptr;
cout << "arr释放后的地址为:" << arr << endl;
}
private:
int* arr; //引入数组,至于存放什么数据我们不关注,只关注它发生的现象
int _year;
int _month;
int _day;
};
int main() {
Data d;
d.Print();
return 0;
}
运行结果:
arr开辟前的地址为:0000000000000000
1.构造函数,给arr,开辟空间
开辟后空间为:0000021A2092C270
2.析构函数,释放arr的空间
arr释放后的地址为:0000000000000000
在定义时,会自动调用构造函数对arr开辟大小为4的空间
在离开main函数后,结束它的生命周期,调用析构函数
为了验证它是在生命周期结束时,才调用析构函数,我们设计一个函数用于观察
void look() {
Data d;
}
int main() {
cout << "main" << endl;
look();
cout << "main" << endl;
return 0;
}
main中调用look,进入look后定义Data类,最后look函数结束,生命周期结束,回到main,打印第二个main:
main
arr开辟前的地址为:0000000000000000
1.构造函数,给arr,开辟空间
2.析构函数,释放arr的空间
arr释放后的地址为:0000000000000000
main
int main() {
cout << "main" << endl;
Data d;
cout << "main" << endl;
return 0;
}
main
arr开辟前的地址为:0000000000000000
1.构造函数,给arr,开辟空间
main
2.析构函数,释放arr的空间
arr释放后的地址为:0000000000000000
这两个例子都能很好的说明,析构函数的作用过程。
至此,构造函数以及析构函数结束,当然还有一些细节没有写,先告一段落。
这两个函数放一块写,主要是这俩一个负责初始化,一个负责销毁。
3.拷贝构造函数
在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
3.1拷贝构造函数的几个特点
3.1.1 拷贝构造函数是构造函数的一个重载形式.
3.1.2 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。一会解释为什么会引发无穷的调用。
3.2拷贝构造函数的实现:
class Time {
public:
Time(int hour,int min ,int sec) {
_hour = hour;
_min = min;
_sec = sec;
}
Time(const Time &t) {
_hour = t._hour;
_min = t._min;
_sec = t._sec;
}
void Print() {
cout << _hour << ":" << _min << ":" << _sec << endl;
}
private:
int _hour;
int _min;
int _sec;
};
int main() {
Time t1(15,22,50);
cout << "t1的time:" << endl;
t1.Print();
Time t2(t1);
cout << "t1的time:" << endl;
t1.Print();
return 0;
}
3.2.1解释为什么传值会引起无穷的递归
通过上面知识的学习,我们可以很清楚的知道一个点:在创建类时,会自动调用构造函数进行初始化,即使构造函数没有写,也会由编译器生成。
那么看这一段代码:(这段代码是错误的,不要模仿)
Time(Time t) {
_hour = t._hour;
_min = t._min;
_sec = t._sec;
}
Time t这个是什么意思?仔细想想便可明白,这就是在创建一个类啊,用于接收传递给它的参数。
如果这个想不明白,可以参考函数传参
void a(int num){
}
这里的int num是不是新创建了一个int变量?答案显而易见。
既然如此,那么Time t也是创建了一个类,既然创建了一个class类,那么编译器就会自动调用拷贝构造函数,把值拷贝给新的class类,以此循环往复,一直在调用自动拷贝,一直在创建新的类。
这里有个问题,为什么编译器会调用拷贝构造函数,而不是调用普通的构造函数或者默认构造函数?。试想,如果我们调用普通的构造函数,那么它还能接收到我们传递给它的参数吗?答:不能,所以这里只能去调用拷贝构造函数。这里是一个坑,不理解这个,就无法理解为什么会无穷递归。
所以只有使用引用才不会陷入递归的过程中,因为我不创建类,就不会让我初始化类,就不会递归下去
3.2.2 const在此处的作用
以防有人将赋值与被赋值的方向搞反,导致原本需要被复制的东西,被覆盖掉。
3.3默认拷贝构造函数:
class Time {
public:
Time(int hour,int min ,int sec) {
_hour = hour;
_min = min;
_sec = sec;
}
void Print() {
cout << _hour << ":" << _min << ":" << _sec << endl;
}
private:
int _hour;
int _min;
int _sec;
};
int main() {
Time t1(15,22,50);
cout << "t1的time:" << endl;
t1.Print();
Time t2(t1);
cout << "t1的time:" << endl;
t1.Print();
return 0;
}
欸!我们会发现为什么默认的拷贝构造函数也能实现将t1复制到t2呀,那我们手写拷贝函数的意义是什么?
3.4浅拷贝与深拷贝
这里又要说一个概念了:
浅拷贝指复制对象时,对象内部的值被复制到一个新的对象中,但对象内部引用的其他对象不会被复制。即,新对象和原对象共享同一个引用对象。因此,如果其中一个对象修改了共享的引用对象,则另一个对象也会受到影响。
深拷贝指复制对象时,对象内部的值和引用对象也都会被复制到一个新的对象中,新对象和原对象互不影响。因此,如果其中一个对象修改了其中一个引用对象,另一个对象不会受到影响。
这么多话,用一句话总结就是:
简单来说,浅拷贝只复制引用,深拷贝则递归地复制对象及其引用的对象。
嗯?什么?看的一知半解?okok,我理解。
3.4.1浅拷贝
举个例子
现在由这么个一个结构体
struct stack {
int* arr;
int size;
int capacity;
};
void text() {
//创建a
struct stack a;
a.arr = (int *)malloc(sizeof(int*) * 10);
a.capacity = 10;
a.size = 0;
cout << "a.arr的地址为:" << &a.arr << endl;
//构建b用于复制a
struct stack b;
b.arr = a.arr;
b.capacity = a.capacity;
b.size = a.size;
cout << "b.arr的地址为:" << &b.arr << endl;
}
int main() {
text();
return 0;
}
可以发现a与b共用arr的空间,此时如果我们在b中free掉arr,那么在a中再去free会出现问题。
同时如果我们在b中修改arr数据,a中的arr数据也会被修改。
这就是浅拷贝:
对于地址,我只拷贝你的地址值
对于int 、double我只拷贝你的字节 。
3.4.2深拷贝
为了解决这一问题,我们需要引入深拷贝,这也是为什么我们要手动实现拷贝构造函数的目的,目的是为了进行深拷贝,给b开辟一块空间,把a中arr的值全部拷入b.arr中
class Time {
public:
Time(int hour,int min ,int sec,int capacity=10) {
_hour = hour;
_min = min;
_sec = sec;
_capacity = capacity;
_arr = (int*)malloc(sizeof(int)*_capacity);
}
//拷贝构造
Time(const Time&t) {
_arr = (int*)malloc(sizeof(int) * _capacity);
memcpy(_arr,t._arr,sizeof(int)*_capacity);
_hour = t._hour;
_min = t._min;
_sec = t._sec;
}
void Print() {
cout << _hour << ":" << _min << ":" << _sec << endl;
cout << "输出地址:" << _arr << endl;
}
~Time()
{
free(_arr);
_capacity = 0;
}
private:
int* _arr;
int _capacity;
int _hour;
int _min;
int _sec;
};
int main() {
Time t1(2023,10,2);
t1.Print();
Time t2(t1);
t2.Print();
return 0;
}
深拷贝中主要用到了:
_arr = (int*)malloc(sizeof(int) * _capacity);
运行结果:
2023:10:2
输出地址:000001DC7CC69350
2023:10:2
输出地址:000001DC7CC56120
可以看出来t1.arr与t2.arr不是同一块地址了。这就实现了深拷贝!
3.4.3 警惕手动实现的拷贝构造函数
拷贝构造函数,依然遵循构造函数的规则:
我们通过拷贝Data类,Data类中的拷贝构造函数就会调用自定义类型Time中的拷贝构造函数,确保自定义类型能够拷贝成功。如果我们的自定义类型中也不存在拷贝构造函数,那么就会发生共用同一块空间这个问题。
通过实验,拷贝构造并不会自动调用自定义类型的拷贝构造,需要手动调用:
为什么这么说呢,我们看看我们自己手写的拷贝构造函数:
这里使用了简化版的模型--2023年10月4号修改
#include <iostream>
using std::cout;
using std::endl;
class Time {
public:
Time() {
_arr[0] = 0;
cout << "初始化Time" << endl;
}
//拷贝构造
Time(const Time& t) {
_arr[0] = 1;//通过这里来验证是否拷贝构造
cout << "开始拷贝Time" << endl;
}
void Print() {
cout << _arr[0] << endl;
}
private:
int _arr[10];
};
class Data {
public:
//构造函数
Data()
{
}
//拷贝构造函数
Data(const Data& d) {
cout << "开始拷贝Data" << endl;
}
void print() {
_time.Print();
}
private:
Time _time;
int _num;
};
int main() {
Data d1;
cout << "初始化成功后的数组首元素的值为:" << endl;
d1.print();
cout << "-----------------------------------------------------" << endl;
Data d2(d1);
cout << "赋值成功后的数组首元素的值为:(在构造函数中,将赋值修改为1)" << endl;
d2.print();
return 0;
}
初始化Time
初始化成功后的数组首元素的值为:
0
-----------------------------------------------------
初始化Time
开始拷贝Data
赋值成功后的数组首元素的值为:(在构造函数中,将赋值修改为1)
0 --按道理说这里应该是1,可是为什么是0呢?
可以发现它并没有进入Time类的拷贝
一句话来概括这种现象:自己实现的拷贝构造函数无法调用到自定义类的拷贝构造函数,需要手动去调用:
#include <iostream>
using std::cout;
using std::endl;
class Time {
public:
Time() {
_arr[0] = 0;
cout << "初始化Time" << endl;
}
//拷贝构造
Time(const Time& t) {
_arr[0] = 1;//通过这里来验证是否拷贝构造
cout << "开始拷贝Time" << endl;
}
void Print() {
cout << _arr[0] << endl;
}
private:
int _arr[10];
};
class Data {
public:
//构造函数
Data()
{
}
//拷贝构造函数
Data(const Data& d) :_time(d._time){
cout << "开始拷贝Data" << endl;
}
void print() {
_time.Print();
}
private:
Time _time;
int _num;
};
int main() {
Data d1;
cout << "初始化成功后的数组首元素的值为:" << endl;
d1.print();
cout << "-----------------------------------------------------" << endl;
Data d2(d1);
cout << "赋值成功后的数组首元素的值为:(在构造函数中,将赋值修改为1)" << endl;
d2.print();
return 0;
}
初始化Time
初始化成功后的数组首元素的值为:
0
-----------------------------------------------------
开始拷贝Time
开始拷贝Data
赋值成功后的数组首元素的值为:(在构造函数中,将赋值修改为1)
1
原因:
Data类中的拷贝构造函数不会调用Time类中的拷贝构造函数,而是调用了Time类的默认构造函数,是因为在Data类的拷贝构造函数中,并没有显式调用Time类的拷贝构造函数。
在拷贝构造函数中,如果没有显式调用成员对象的拷贝构造函数,会使用默认构造函数进行成员对象的初始化。因此,在Data类的拷贝构造函数中,并没有对Time类的拷贝构造函数进行调用,而是直接使用了Time类的默认构造函数来初始化成员对象_time。
但编译器自动实现的Data类的拷贝,却可以自动调用Time类的拷贝
类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请 时,则拷贝构造函数是一定要写的,否则就是浅拷贝
#include <iostream>
using std::cout;
using std::endl;
class Time {
public:
Time() {
_arr[0] = 0;
cout << "初始化Time" << endl;
}
//拷贝构造
Time(const Time& t) {
_arr[0] = 1;//通过这里来验证是否拷贝构造
cout << "开始拷贝Time" << endl;
}
void Print() {
cout << _arr[0] << endl;
}
private:
int _arr[10];
};
class Data {
public:
//构造函数
Data()
{
}
void print() {
_time.Print();
}
private:
Time _time;
int _num;
};
int main() {
Data d1;
cout << "初始化成功后的数组首元素的值为:" << endl;
d1.print();
cout << "-----------------------------------------------------" << endl;
Data d2(d1);
cout << "赋值成功后的数组首元素的值为:(在构造函数中,将赋值修改为1)" << endl;
d2.print();
return 0;
}
初始化Time
初始化成功后的数组首元素的值为:
0
-----------------------------------------------------
开始拷贝Time
赋值成功后的数组首元素的值为:(在构造函数中,将赋值修改为1)
1
2023/11/11修改--从此处开始:
#include <iostream>
using std::cout;
using std::endl;
class Time {
public:
Time() {
_arr[0] = 0;
cout << "初始化Time" << endl;
}
//拷贝构造
Time(const Time& t) {
_arr[0] = 1;//通过这里来验证是否拷贝构造
cout << "开始拷贝Time" << endl;
}
Time& operator = (const Time &t){
_arr[0] = t._arr[0];
_arr[0] = 1;//通过这里来验证是否拷贝构造
return *this;
}
void Print() {
cout << _arr[0] << endl;
}
private:
int _arr[10];
};
class Data {
public:
//构造函数
Data()
{
}
//拷贝构造函数
Data(const Data& d) {
cout << "开始拷贝Data" << endl;
_time = d._time;
}
void print() {
_time.Print();
}
private:
Time _time;
int _num;
};
int main() {
Data d1;
cout << "初始化成功后的数组首元素的值为:" << endl;
d1.print();
cout << "-----------------------------------------------------" << endl;
Data d2(d1);
cout << "赋值成功后的数组首元素的值为:(在构造函数中,将赋值修改为1)" << endl;
d2.print();
return 0;
}
还有一种方法:我们在拷贝构造函数里面明确写上"="赋值操作符,通过这个去显式调用,这个会跳转到Time类的赋值"="函数里面
我以目前的认知,认为拷贝构造函数出现在没有自定义类成员的时候好用很多。--2023年10月-2号。等我有新的认知我再回来修改。
啊哈,我回来了,其实方法很简单,只要我们在构造函数里面显式的实现"="操作,即可
2023年11月11日--修改结束
4 运算符重载
4.1特点
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)。
不能通过连接其他符号来创建新的操作符:比如operator@
重载操作符必须有一个类类型参数
用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
" * :: sizeof ?: . " 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
4.1.1解释重载操作符必须有一个类类型参数。
如果我们要重载两个int类型相加
int operator + (const int a1,const int a2){
return a1+a2;
}毫无意义
甚至有人会写为
int operator -(const int a1,const int a2){
return a1+a2;
}
所以必须要有一个自定义类型作为参数!
4.2运算符重载的实现
4.2.1运算符重载的全局实现以及缺点:
class Time {
public:
Time(int hour,int min,int sec) {
_hour = hour;
_min = min;
_sec = sec;
}
int _hour;
int _min;
int _sec;
};
bool operator ==(const Time& t1, const Time& t2) {
return t1._hour == t2._hour &&
t1._min == t2._min &&
t1._sec == t2._sec;
}
int main() {
Time t1(21,27,20);
Time t2(21,27,20);
cout << (t1 == t2) << endl;
return 0;
}
这是一个简单的运算重载符。
缺点:这个重载符是个全局变量,在这里如果我们要用class类去运算,则必须要求class里的成员变量为public,这样才能访问到它的成员函数变量。但是这样就无法保证成员数据的安全性。
所以我们要把它定义在class类里面。
bool operator ==(const Time& t2) {
return this->_hour == t2._hour &&
this->_min == t2._min &&
this->_sec == t2._sec;
}
为什么这里的运算符重载变为一个参数了?
因为在class类里面,对于每个函数的参数,都有一个隐含的参数即,*this。
bool operator ==(const Time*this,const Time& t2) {
return this->_hour == t2._hour &&
this->_min == t2._min &&
this->_sec == t2._sec;
}
只不过这个this被隐藏掉了,所以不用写。
这里需要注意的是,左操作数是this,指向调用函数的对象。在代码中可以把this省略,在这里写出来是为了方便理解以及记忆。
bool operator ==(const Time& t2) {
return _hour == t2._hour &&
_min == t2._min &&
_sec == t2._sec;
}
4.2.2 赋值运算符:
注意事项:
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,
有返回值目的是为了支持连续赋值
检测是否自己给自己赋值 返回*this :要复合连续赋值的含义
4.2.3 赋值运算符的实现
--此版本不包含自定义变量、也不包含指针。此处的赋值全是浅拷贝
class Time {
public:
Time() {
}
Time(int hour,int min,int sec) {
_hour = hour;
_min = min;
_sec = sec;
}
Time& operator =(const Time& t2) {
if (this != &t2) { //这里的&不是引用而是取地址的意思。
//如果是自己赋值给自己,则可以不用赋值,直接跳过即可
_hour = t2._hour;
_min = t2._min;
_sec = t2._sec;
}
return *this;
}
private:
int _hour;
int _min;
int _sec;
};
int main() {
Time t1(21,27,20);
Time t2 ;
t2 = t1;
return 0;
}
赋值运算符只能重载成类的成员函数不能重载成全局函数。
原因为:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现 一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值 运算符重载只能是类的成员函数。
4.2.4 赋值运算符也符合上面的规则:
1.如果我们不自己实现,编译器会帮我们实现一个默认的赋值运算符
2.如果在类中包含自定义类,则需要自己手动调用自定义类
Data &operator=(const Data&d1){
_day = d1._day;
_time = d1._time;//手动调用
return *this;
}
3.如果自定义类型中存在数组等,需要自己实现深拷贝,这个和之前实现过的逻辑差不多,就不再演示。
还请自己区分什么时候会调用拷贝构造函数,什么时候会调用赋值运算符!!!
这里不再演示。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_39117115/article/details/133484338
————————————————
版权声明:本文为CSDN博主「蠢 愚」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39117115/article/details/133484338