7.C++面向对象3(拷贝构造函数,赋值运算符重载)

⭐本篇为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	:?	.  这五个操作符不可重载

三.下章重点

3.1 const成员

3.2 取地址重载 

上一篇:生信初学者教程(二十三):REF+SVM筛选候选标记物-介绍


下一篇:系统架构设计师-下午案例题(2018年下半年)