⭐本篇为C++学习第7章,主要了解 拷贝构造函数,赋值运算符重载
⭐本人Gitee C++代码仓库:yzc的c++学习: 小川c++的学习记录 - Gitee.com
上篇讲了6个默认成员函数的构造函数和析构函数。
重要代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
~Date()
{}
void print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
return 0;
}
一. 拷贝构造函数
a 拷贝构造函数是构造函数的一个重载,用于对象的拷贝并且初始化
如:
Data d1(2024,10,1); Data d2(d1);
b 拷贝构造函数在的参数只有一个且必须使用引用传参,传值会导致无穷递归调用
c 如果用户没有显示定义,编译器会生成默认的拷贝构造函数,但这个函数以内存存储,字节序进行浅拷贝
若使用按值传参会导致无穷递归调用
如:
这是因为传参的时候,需要调用拷贝构造函数对参数进行拷贝,又需要调用拷贝构造函数
正确写法如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
~Date()
{}
void print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024, 10, 11);
Date d2(d1);
d1.print();
d2.print();
return 0;
}
使用引用传参可以防止无穷递归问题,再使用const可以防止修改d中的内容
运行结果如下:
和析构函数一样,拷贝构造函数也存在深浅拷贝问题。
如果我们在堆上申请空间,并且使用了浅拷贝。这就会导致同一块地址空间被析构两次而报错
二. 运算符重载
2.1 其他运算符重载
在了解赋值运算符重载之前要知道什么是运算符重载。
c++为了增强代码可读性引入了运算符重载,运算符重载也是一种函数
函数原型: 返回值类型 operator 运算符符号
运算符符号:如 + - * / == > < 等
为什么要有运算符重载??
比如我们定义了一个日期类,如何判读两个日期对象是否相等??如何比较两个日期的大小??
如:
很明显需要我们需要自己定义 == 来完成这个功能
==运算符重载
所以我们很容易写出下面的函数原型,但是这是错误的!
bool operator==(const Date& d1, const Date& d2)
{
}
因为我们调用的方式是 d1 == d2
d1 == d2 明显是d1去调用==这个函数来判断和d2是否相等,所以d1已经被this指针传入了,我们无需再传入
正确方法如下:
bool operator==(const Date& d)
{
//通过this指针来传入第一个参数
return this->_year = d._year
&& this->_month = d._month
&& this->_day = d._day;
}
测试
>运算符重载
bool operator > (const Data& d)
{
if (_year > d._year)
return true;
else if (_year == d._year && _month > d._month)
return true;
else if (_year == d._year && _month == d._month && _day > d._day)
return true;
else
return false;
}
有了==运算符重载可以轻易写入>运算符重载,体现了代码的复用
<运算符重载
由>运算符重载,我们能轻易写入<运算符重载
bool operator<(const Date& d)
{
if (_year < d._year)
return true;
else if (_year == d._year && _month < d._month) //这里直接调用==重载符
return true;
else if (_year == d._year && _month == d._month && _day < d._day)
return true;
else
return false;
}
2.2 赋值运算符重载
赋值运算符重载的意义是用一个对象赋值给另一个对象。
赋值运算符重载和拷贝构造函数要区分。
由于赋值运算符重载是6个默认成员函数之一,如果用户没有自定义,系统会按字节序进行浅拷贝
int main()
{
Date d1(2024, 10, 11);
Date d2(1, 1, 1);
d2 = d1; //赋值运算符重载,d2已经初始化
Date d3(d1);//拷贝构造,d3还没有初始化
return 0;
}
void operator=(const Date& d)
{
//如果this指向对象的地址和d的地址不一样才能进行赋值
//如果地址一样说明二者相等(不仅仅是值相同,连内存地址都一样!),无需赋值
//我们需要完成的是深拷贝,两个对象仅仅是值相同,内存地址不一样
if (this != &d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
}
简单测试:
但是上面代码还完美。比如我们经常有下列操作
按照上面的写法会报错,因为我们返回值是void
当(d2=d1)后执行 d3=void,这显然是不对的。我们需要返回d2才能完成 d3=d2
//使用引用返回可以减少拷贝,增加效率
Date& operator=(const Date& d)
{
//如果this指向对象的地址和d的地址不一样才能进行赋值
//如果地址一样说明二者相等(不仅仅是值相同,连内存地址都一样!),无需赋值
if (this != &d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
//this执行被赋值的对象,解引用就能找到它
return *this;
}
简单测试
可以看到,能够完成多次赋值
注意:
a.不能连接其他操作符构成新的操作符如operator@ b.重载操作符必须用于自定义类类型 c.用于内置类型的操作符,其含义不可改变,如+不能改变含义 d.作为类成员的重载函数时,其参数要少一个。由于有一个默认参数this e. .* :: sizeof :? . 这五个操作符不可重载