当前位置: 首页 > news >正文

【C++初阶】类和对象③之运算符重载--从日期类推广自定义类型运算的方法奥秘

1.c++运算符重载

对于+ - * /这样运算符内置类型可以直接使用,但是自定义类型不可以直接使用

内置类型:

int a = 1;

int b = 2;

c = a+ b;

但是 Date d1 Date d2

d1 == d2这样就不可以直接使用。

因为对于内置类型来说,编译器知道其类型的含义就可以进行比较,直接就转换成指令了

但是对于自定义类型来说,编译器不知道要按照什么样的规则来进行比较。所以C++为了增强代码的可读性引入了运算符重载。

运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

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

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

注意:

不能通过连接其他符号来创建新的操作符:比如operator@

重载操作符必须有一个类类型参数

用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义

作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this

那么日期类的比较就可以这样来写,关于日期的比较不像自定义类型那些谁大就大,而是有一个比较逻辑,编译器是不能确定我们自己定义的类的比较逻辑的,无法统一所以就无法比较。

①>

bool operator>(const Date& d1, const Date& d2)
{
	if (d1._year > d2._year)
	{
		return true;
	}
	if (d1._year == d2._year && d1._mouth > d2._mouth)
	{
		return true;
	}
	if (d1._year == d2._year && d1._mouth == d2._mouth && d1._day > d2._day)
	{
		return true;
	}
	return false;

}

此时的c++可以支持这样写;

 此时的使用就和我们的内置类型使用运算符感觉没有差异了。

当我们将这个函数的实现放在类里面就会报错:

因为还有一个隐藏的this指针,参数要和操作数匹配,所以这样修改:

bool operator==( const Date& d2)
{
	return _year == d2._year
		&& _mouth == d2._mouth
		&& _day == d2._day;
}
bool operator>( const Date& d2)
{
	if (_year > d2._year)
	{
		return true;
	}
	if (_year == d2._year && _mouth > d2._mouth)
	{
		return true;
	}
	if (_year == d2._year && _mouth == d2._mouth && _day > d2._day)
	{
		return true;
	}
	return false;

}

 调用时这样调用:

d1.operator>(d2);

调用的时候可以显示调用。隐式调用,编译器也会如上转化

可以重载那些运算符,主要看这个运算有没有意义,有意义就可以实现,没有意义就不要实现。

②赋值:

赋值运算符的重载:两个已经存在的对象进行拷贝。而拷贝构造是一个已经1存在的对象去拷贝初始化另外一个对象。

Date& operator=(const Date&d)
{
	if (this != &d)
	{
		_year = d._year;
		_mouth = d._mouth;
		_day = d._day;
		
	}
	return *this;
	
}

赋值函数是一个默认构造函数,operate= 我们不写编译器会默认生成一个。行为跟拷贝构造类型,对内置类型完成值拷贝,对自定义类型完成他的赋值。date可以不写,myqueque1不写,栈要写,完成深度拷贝。

③日期类的前置++和后置++

两个函数名在书写形式上无法区分,c++规定,operater加运算符是函数名,必须构成函数重载,加一个参数, 加一个类型,c++规定,前置++调用无参数的,后置++调用有参数的那个。

④流插入流提取:

在类的打印中,我们使用的是这样的方式:

但是这样却不行:

原因:

里的《 》也是运算符,输出输入运算符,我们的d1是自定义类型,自定义类型都不支持运算符,如果要支持运算符就得写运算符重载:

cout cin 分别是istream 和ostresm类型的对象。

内置类型能够使用的原因也是因为库里面已经写了实现了。c++输入输输出支持自动类型识别也是因为: 函数重载,参数匹配。

自己实现流插入运算符重载:

void Date::operator<<(ostream& out)
{
	cout << _year << "年" << _mouth << "月" << _day << "日" << endl;
}

双操作数运算符规定:第一个参数是左操作数,第二个参数是右操作数。所以,原因:*this将第一个参数位置占用了。但是并没有规定运算符重载必须是成员函数,流插入实现成成员函数是不好的,这种写法代码可读性很低。将私有变为公有可以采取下面这样的实现方式也就是使用全局函数。

也可以通过友元函数定义成类外部函数,实现流插入运算符重载。(友元函数会在后面文章讲解)

首先在声明中添加友元函数:

friend void operator<<(ostream& out,const Date&d);

简单理解:

我是你的朋友,那么我可以访问你的私有

接着, 流插入一般支持这样的写法:cout<<d1<<d2.

运算符重载原则支持连续的+=,支持连续的赋值,连续的+=都是返回值支持的。赋值是从右往左,但是《是从左向右

cout<<d1  这里是转换成函数调用,函数调用应该有个返回值,返回值应该是cout,然后作为第二次调用的左操作数,来支持连续的cout<<d1<<d2.

ostream& operator<<(ostream& out, const Date& d)
{
	cout << d._year << "年" << d._mouth << "月" << d._day << "日" << endl;
	return out;
}

流提取实现:

istream& operator << (istream& in, Date& d)
{
	cin >> d._year >> d._mouth >> d._day;
	return in;
}

所以:

其他运算符一般是实现为成员函数,>> <<流运算符必须是实现到全局,这样才能让流对象做第一个参数,才符合可读性。(友元函数可以解决访问私有的问题)。

为什么c++要搞流插入提取,不用printf

c++的流本质是为了解决,自定义类型的输入和输出问题,printf无法解决自定义类型的输入输出问题。

运算符重载注意提醒

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数,所以不可以这样写
    int operator-(int i ,int j)

    也就是说不能通过重载运算改变内置类型的运算规则。比如本来编译器要做+但是被重载成了-就会乱套

  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this
  • .*  ::  sizeof  ?:  . 注意以上5个运算符不能重载。

2.实现一个完整的日期类 


2.1构造函数

首先实现构造函数,我们采取在类内声明,类外定义的方式来实现,使得代可读性更好

报错原因:缺省参数不可以在定义和声明的时候同时给,会冲突,所以只用在声明的时候给出就好。同时由于在类外使用要声明类域:

测试: 

2.2 获取天数的函数

 

2.3 拷贝构造函数 

测试:

2.4 析构函数 

由于日期类没有需要释放的资源所以直接使用默认生成的析构函数就好

~Date()
{
	printf("析构函数起作用\n");
}

2.5日期类的赋值运算

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

}

2.6 实现日期+=天数

测试:

 2.7 实现日期+函数

注意+自身的值是不会改变的

Date Date:: operator+(int day)
{
	Date tmp = *this;
	tmp._day += day;//天数先相加,再折算年月
	while (tmp._day > GetDay(_year, _month))
	{
		tmp._day -= GetDay(_year, _month);
		++tmp._month;
		if (tmp._month == 13)
		{
			tmp._year++;
			tmp._month = 1;
		}
	}
	return tmp;

}

c测试:

实现方式二:复用+=:

Date Date:: operator+(int day)
{
	Date tmp = *this;
	tmp += day;
	//tmp._day += day;//天数先相加,再折算年月
	//while (tmp._day > GetDay(_year, _month))
	//{
	//	tmp._day -= GetDay(_year, _month);
	//	++tmp._month;
	//	if (tmp._month == 13)
	//	{
	//		tmp._year++;
	//		tmp._month = 1;
	//	}
	//}
	return tmp;

}

 同样+=也可以使用我们的+来实现:

*this = *this+day;
return *this;

但是明显使用+=来实现+效率是更好的,因为+=实现是使用的是引用返回并且使用的赋值运算少,所以在过程中会少拷贝操作,效率也会更高。

2.8 实现日期-=天数 

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

	}
	return *this;
}

测试:

2.9实现日期—天数

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

	}
	return tmp;

}

2.10比较两个日期大小 ==(相等)

bool Date::operator==(const Date& d)
{

	
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	
}

2.11比较两个日期大小 >(大于)

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


}

2.12比较两个日期大小  <(小于)

直接复用大于和等于

//比较两个日期大小? < (小于)
bool Date:: operator<(const Date& d)
{
	return !(*this >= d);
}

2.13比较两个日期大小 <=(小于等于)

//比较两个日期大小 <= (小于等于)
bool Date::operator<=(const Date& d)
{
	return !(*this > d);
}

2.14比较两个日期大小 >=(大于等于)

//比较两个日期大小 >= (大于等于)
bool Date::operator>=(const Date& d)
{
	return *this == d || *this > d;

}

2.15比较两个日期大小 !=(不相等)

//比较两个日期大小 != (不相等)
bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

2.16 实现前置++

//实现前置++
Date Date:: operator++()
{
	*this += 1;
	return *this;//前置++是先++后使用

}

2.17 实现后置++

后置++是使用后改变

Date Date::operator++(int)
{
	Date tmp(*this);
	*this += 1;
	return tmp;

}

减减和加加是一样的就不在实现

2.18实现日期减去日期

//实现日期减去日期
int Date::operator-( Date& d)
{
	//先假设左大右小
	int flag = 1;
	Date max = *this;
	Date min = d;
	//如果假设错了就调整一下
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}
	return n * flag;
}

相关文章:

  • 使用Navicat for MySQL工具连接本地虚拟机上的MySQL
  • mybatis 入门案例
  • 磁电偶极子学习笔记2 60GHz 双极化 二维转换波束 口径耦合 磁电偶极子宽带天线阵列
  • 云平台结合DeepSeek的AI模型优化实践:技术突破与应用革新
  • Redis中的某一热点数据缓存过期了,此时有大量请求访问怎么办
  • 简述mysql主从复制原理及工作过程,配置一主两从并验证
  • 封装红黑树实现map和set
  • 缺陷检测之图片标注工具--labme
  • 【python】You-Get
  • 使用京东AsyncTool实现异步编排
  • 4、IP查找工具-Angry IP Scanner
  • 用deepseek学大模型03-数学基础 概率论 条件概率 全概率公式 贝叶斯定理
  • 周雨彤:用角色与生活,诠释审美的艺术
  • 1、cadence从零开始让一个VCO起振——基本设置
  • MATLAB算法实战应用案例精讲-【数模应用】空间插值(附MATLAB、R语言和python代码实现)
  • JavaScript设计模式 -- 迭代器模式
  • 【信息学奥赛一本通 C++题解】1285:最大上升子序列和
  • 同花顺数据爬取并生成K线
  • 【SpringBoot苍穹外卖】debugDay0 打开前端页面
  • sed命令详细教程
  • 滨江集团:一季度营收225.07亿元,净利润9.75亿元
  • 铁路迎来节前出行高峰,今日全国铁路预计发送旅客1870万人次
  • 陕西省副省长窦敬丽已任宁夏回族自治区党委常委、统战部部长
  • 举牌超200轮!中铁建7.76亿元竞得北京通州梨园宅地
  • 张元济和百日维新
  • 长三角铁路“五一”假期运输今启动:预计发送旅客量增6%,5月1日当天有望创新高