【C++】类和对象(三)运算符重载 (其中有默认成员函数中的 赋值运算符重载、等等)

🧸🧸🧸 各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

    今天的内容是运算符重载,内置类型可以直接使用运算符运算,因为编译器知道该如何进行运算,自定义类型无法直接使用运算符,编译器也不知道该如何运算,如果想要让编译器支持,就需要自己实现运算重载

一、概念

C++为了增加代码的可读性增加了运算符重载,运算符重载是具有特殊函数名的函数,也具有返回类型,函数名字以及参数列表,其返回值和参数列表与普通函数相似,最大的不同就是它具有特殊的函数名

函数名为:关键字operator后跟需要重载的运算符
返回值和参数列表由操作符的性质决定

二、类里的private无法访问的解决办法

1.类中提供获取成员变量的函数,来返回成员变量
2.友元、但是友元破坏了封装,所以不建议使用
3.将函数写在类里

三、各种运算符重载

运算符重载的第一个参数也就是this指针是在运算符前面的,比如==的运算符重载
d1==d2; d1就是this指针的参数

1.==运算符重载

对于大结构体来说,传引用是很提高效率的,并且里面不需要改变,最好加上const,保证安全(保证里面不会写错),另外,因为this指针我们不能显示写,所以C++中为了修饰this指针指向的内容不能改变,就只能加在后面,如下所示

class Date
{
public:
	Date(int year=1,int month=1,int day=1)
	{
		_year=year;
		_month=month;
		_day = day;
	}
	
	bool operator==(const Date&d)  const
	{
		return _year==d._year
			&& _month==d._month
			&& _day==d._day;   
	}

private:
	int _year;
	int _month;
	int _day;
};

2.+=运算符重载

可以看出,+=是可以实现连续加等的,所以要传返回值
在这里插入图片描述

 class  Date
{
public:

	Date(int year=1, int month=1, int day=1)
		:_year(year)
		, _month(month)
		, _day(day)//初始化列表,后面讲
	{
	}

	bool isLeapYear(int year)
	{
		return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
	}
	size_t getMonthDay(size_t year, size_t month)
	{
		if (isLeapYear(year) && month == 2)
			return 29;
		return arr[month];

	}
	//当然这里不传引用也是可以
	Date& operator+=(const size_t& day)
	{
		_day += day;
		while (_day > getMonthDay(_year, _month))
		{
			_day -= getMonthDay(_year, _month);
			_month++;
			if (_month == 13)
				_year++;
		}
		return *this;//因为支持连续+=;

	}
private:
	int _year;
	int _month;
	int _day;
	static int arr[13];//定义一个静态的数组,方便获得每月的天数
};
int Date::arr[13] = { 0,31,28,31,30,31,20,31,31,30,31,30,31 };

3.+运算符重载

 class  Date
{
public:

	Date(int year=1, int month=1, int day=1)
		:_year(year)
		, _month(month)
		, _day(day)//初始化列表,后面讲
	{
	}

	bool isLeapYear(int year)
	{
		return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
	}
	size_t getMonthDay(size_t year, size_t month)
	{
		if (isLeapYear(year) && month == 2)
			return 29;
		return arr[month];

	}
	//当然这里不传引用也是可以
	Date operator+(const size_t& day) const
	{
		Date ret=*this;//拷贝构造
		ret._day += day;
		while (ret._day > getMonthDay(ret._year, ret._month))
		{
			ret._day -= getMonthDay(ret._year, ret._month);
			ret._month++;
			if (ret._month == 13)
				ret._year++;
		}
		return ret;

	}
private:
	int _year;
	int _month;
	int _day;
	static int arr[13];//定义一个静态的数组,方便获得每月的天数
};
int Date::arr[13] = { 0,31,28,31,30,31,20,31,31,30,31,30,31 };

其他的普通的运算符重载我就不写了
在这里插入图片描述


三、赋值运算符重载

赋值运算符重载只能重载与成员,不能重载于全局,因为赋值运算符重载是默认成员函数,不写也会自动生成,所以,重载于全局的话会起冲突!
注意,赋值也是可以连续赋值的

 class  Date
{
public:

	Date(int year=1, int month=1, int day=1)
		:_year(year)
		, _month(month)
		, _day(day)//初始化列表,后面讲
	{
	}
	Date& operator=(const Date& d)//支持连续赋值
	{
		if(this!=&d)
		{
			_year=d._year;
			_month=d._month;
			_day=d._day;
		}
		return *this;
	}
	
private:
	int _year;
	int _month;
	int _day;
};

赋值运算符重载也是默认成员函数,我们不显示写时编译器默认生成,以值拷贝(浅拷贝)的方式逐字节进行,同样,对于内置类型直接浅拷贝,对于自定义类型,调用对应的默认赋值运算符重载函数完成赋值,所以像日期类这种就不需要自己写了,但是如果是自己开辟空间的那种,就需要自己来实现深拷贝完成赋值。
原因:
1.浅拷贝拷贝出来的地址和参数是相同的,所以会存在可以互相修改的问题
2.会被free或者delete两次,报错


四、对于前四个默认成员函数的总结

1.大部分的类都要自己去实现构造函数,除了像用两个栈来实现队列的这一种类,这个队列会去
调用栈的构造函数,所以不用自己写。
2.每个类最好都提供不传参数的构造函数
    a.编译器生成的构造
    b.自己写的无参构造
    c.全缺省构造
3.对于析构,只需要自己申请了空间的类去写,包括两个栈实现队列的也不用写(栈要写,队列不写),而栈帧和栈的性质相似,所以先定义的后析构,后定义的先析构。
4.拷贝构造函数其实是构造函数的重载
对于重载,发现void func(int a)和void func(int&aa)构成重载,因为函数名的修饰规则不同
但是又发现typeid(a).name()和typeid(aa).name()都是int

5.拷贝构造和赋值运算符重载对于不需要深拷贝的类就不用写,默认使用的浅拷贝足够
6.很多运算符重载都是可以复用的,比如说+=和+,> >= < <=.


五、对于前置++和后置++的运算符重载

为了对前置++和后置++和前置–和后置–做区分,C++对于这一点利用函数重载去区分

Date& operator++();//前置
Date& operator++(int);//后置

六、流插入<<和流提取>>的运算符重载

首先说明
cin和cout是对象而不是函数
cin是istream的对象
cout是ostream的对象

流插入<<和流提取>>需要写成全局的,因为在类里,cin无法作为左操作数,而cin或者cout

cout<<1<<2<<3<<endl;
cin>>a>>b>>c;
//因为cin和cout需要当左操作数,所以写成全局的,不受this指针影响。

所以正确写法:

class Date()
{
	friend ostream& operator<<(ostream&out,const Date& d);
public:
	Date(int year=1,int month=1,int day=1)
		:_year(year)
		,_month(month)
		,_day(day)
	{
	}	


private:
	int _year;
	int _month;
	int _day;
};
//cout与cin类似
inline ostream& operator<<(ostream&out,const Date& d)
{
	//因为在类外,获得private成员变量三种方式
	1.类里写get的函数
	2.友元,不建议使用,破坏封装,但是这里用一下友元
	3.私有改公有
	out<<d._year<<'.'<<d._month<<'.'<<d._day<<endl;
	return out;
}

因为会频繁的调用,所以写成内敛,但是注意,这里不能够声明于定义分离,因为内敛不会进符号表。如果分离,那么在链接的时候找不到函数地址而出现链接错误


七、运算符重载注意事项

1.运算符重载不能够去创造新的运算符
2.重载操作必须有一个类类型参数
3.重载的含义不能改变,比如说+去实现*
4.有隐含的this指针
5.有几个操作符是不能够被重载的
    a.域作用限定符 ::
    b.sizeof()
    c.三目运算符 ?:
    d. .点
    e. .*点乘

八、取地址&运算符重载

对于这个运算符来说,因为他是默认成员函数,编译器会自己生成,并且,编译器自己生成的也就够用了,所以一般不用我们自己写,除非遇到特殊的场景:比如我不想让别人拿到我的地址:

//const在*之前修饰指针指向的内容
//const在*之后修饰指针
A* operator&()//Date *const this默认参数
{
	return nullptr;
}

九、总结

上面对C++运算符重载重要知识点进行了总结,感谢大家的支持,后面我会继续更新类和对象有关方面的内容!
在这里插入图片描述

  • 36
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 35
    评论
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪皮兄弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值