C++入门 类--赋值运算符重载(上)

目录

运算符重载

概念

 赋值运算符重载

格式

传值返回和传引用返回

实践总结★

赋值运算符只能重载成类的成员函数

编译器会生成一个默认赋值运算符重载

部分日期类实现


如果需要比较一个日期谁在前谁在后应该怎么实现呢?先看以下代码:

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//private:
		// 给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

// 大于  //dt1比dt2晚就返回true,反之返回false
bool DateGreater(const Date& dt1, const Date& dt2)
{    
	if (dt1._year > dt2._year)
	{
		return true;
	}
	else if (dt1._year == dt2._year)
	{
		if (dt1._month > dt2._month)
		{
			return true;
		}
		else if (dt1._month == dt2._month)
		{
			return dt1._day > dt2._day;
		}
	}
	return false;
}

int main()
{
	Date d1(2024, 4, 12);
	Date d2(2024, 4, 10);
	cout << DateGreater(d1, d2) << endl; //输出1
	return 0;
}

但是,我们注意到Date类中成员变量变成了public,并且一旦去除private的注释,编译器会发生下图警告

这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证? 

 因此我们引入一个新的概念:赋值运算符重载

运算符重载

概念

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字operator后面接需要重载的运算符符号。

函数原型:返回值类型 operator操作符(参数列表) 

现在对上面代码进行修改:

//两函数名实现功能相同
//bool DateGreater(const Date& dt1, const Date& dt2)
bool operator>(const Date& dt1, const Date& dt2)

//三句语句表达意思相同
//cout << DateGreater(d1, d2) << endl;
cout << operator>(d1, d2) << endl; 
cout << (d1 > d2) << endl;         

 但是和上面问题一样,类成员变量只能是public,变成private会报错。这时候可以利用上几篇的经验,将运算符重载函数放在类里实现。代码如下:

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

    // bool operator>(Date* this, const Date& dt)
	bool operator>(const Date& dt)
	{
		//下面语句相同  都存在一个this指针
        //this就是d1,dt就是d2
		//if (this->_year > dt._year) 
		if (_year > dt._year)
		{
			return true;
		}
		else if (_year == dt._year)
		{
			if (_month > dt._month)
			{
				return true;
			}
			else if (_month == dt._month)
			{
				return _day > dt._day;
			}
		}
		return false;
	}
	private:
		// 给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

int main()
{
	Date d1(2024, 4, 12);
	Date d2(2024, 4, 10);
    
    //两者等价
	cout << d1.operator>(d2) << endl;     //显式调用
	cout << (d1 > d2) << endl;            //转换调用
	return 0;
}

这样就能够成功实现我们需要实现的功能了。

注意:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义,否则会破坏编译器计算规则
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  5. .*(二元运算符 (一般遇不到)  ::(作用域符)   sizeof(数据类型长度符)   ?:(三目运算符)  .(成员访问运算符) 注意以上5个运算符不能重载。
  6. 运算符重载需要有实际意义,如:两个日期相乘没有任何意义

 赋值运算符重载

格式

参数类型:const Type &,传递引用可以提高传参效率

返回值类型:Type &,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值

检测是否自己给自己赋值

返回*this :要复合连续赋值的含义

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	Date& operator=(const Date& d)
	{
		if (this != &d)		//避免出现d1 = d1这种情况
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 4, 14);

	// 拷贝构造		//这里调用拷贝构造函数
	// 一个已经存在的对象,拷贝给另一个要创建初始化的对象
	Date d2(d1);
	Date d3 = d1;

	Date d4(2024, 5, 1);

	// 赋值拷贝/赋值重载	//这里调用赋值运算重载
	// 一个已经存在的对象,拷贝赋值给另一个已经存在的对象
	d1 = d2 = d4;

	return 0;
}

此处需要区分拷贝构造和赋值重载的区别。

传值返回和传引用返回

 在给ref赋值时,可以看到上面两个func函数返回值的区别,引用对应静态区变量,Date对应栈区变量,如果使用Date&返回值接收Date,会出现错误,原因是因为Date d只在func函数存在,出了该区就销毁了,总的来说,虽然引用返回减少了一次拷贝,但是出了作用域,对象还存在,才能使用引用返回

实践总结★

  1. 出了作用域,返回对象还在没有析构,那就可以用引用返回,减少拷贝

  2. 返回对象生命周期到了,会析构,传值返回

  3. 返回对象生命周期没到,不会析构,传引用返回

赋值运算符只能重载成类的成员函数

赋值运算符只能重载成类的成员函数不能重载成全局函数,赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现 一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值 运算符重载只能是类的成员函数。

编译器会生成一个默认赋值运算符重载

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符 重载完成赋值。

跟拷贝构造类似:Date / MyQueue 默认生成的赋值就够用了,但是Stack / List 等自定义类型都需要我们自己实现赋值重载。

部分日期类实现

//Date.h
#pragma once

#include<iostream>
#include<assert.h>

using namespace std;

class Date
{
public:
	Date(int year = 2000, int month = 1, int day = 1);
	void Print();
	// 直接定义类里面,他默认是inline
	// 频繁调用
	int GetMonthDay(int year, int month)
	{
		assert(month > 0 && month < 13);

		static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

		// 365天 5h +  //闰年 | 平年判断
		if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
		{
			return 29;
		}
		else
		{
			return monthDayArray[month];
		}
	}

	bool operator<(const Date& d);
	bool operator<=(const Date& d);
	bool operator>(const Date& d);
	bool operator>=(const Date& d);
	bool operator==(const Date& d);
	bool operator!=(const Date& d);

	Date& operator+=(int day);
	Date operator+(int day);
	Date& operator-=(int day);
	Date operator-(int day);

private:
	int _year;
	int _month;
	int _day;
};
//Date.cpp
#define _CRT_SECURE_NO_WARNINGS

#include"t.h"

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
}

void Date::Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}

bool Date::operator<(const Date& d)
{
	if (_year < d._year)
	{
		return true;
	}
	else if (_year == d._year)
	{
		if (_month < d._month)
		{
			return true;
		}
		else if (_month == d._month)
		{
			return _day < d._day;
		}
	}
	return false;
}

bool Date::operator<=(const Date& d)
{
	return *this < d || *this == d;
}

bool Date::operator>(const Date& d)
{
	return !(*this <= d);
}

bool Date::operator>=(const Date& d)
{
	return !(*this < d);
}

bool Date::operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}

bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

Date& Date::operator+=(int day)
{
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		++_month;
		if (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;
}

//Date Date::operator+(int day)
//{
//	Date tmp = *this;
//	tmp._day += day;
//	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//	{
//		tmp._day -= GetMonthDay(tmp._year, tmp._month);
//		++tmp._month;
//		if (tmp._month == 13)
//		{
//			++tmp._year;
//			tmp._month = 1;
//		}
//	}
//	return tmp;
//}

//与上面实现功能相同
Date Date::operator+(int day)
{
	Date tmp = *this;
	tmp += day;
	return tmp;
}

Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_day += GetMonthDay(_year, _month);
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
	}
	return *this;
}

Date Date::operator-(int day)
{
	Date tmp = *this;
	tmp -= day;
	return tmp;
}

单目运算符++和--下篇文章会继续记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值