类和对象(三)运算符重载
魈十三 2024-08-22 14:05:03 阅读 75
上一篇博客的补充说明:引用虽然和指针的功能相差无几,但是引用不需要开空间,单纯只是给变量取一个别名,当然引用也可以作为形参传递。缺省函数必须从右到左一次给,不能出现左边有,右边没有的情况。本质上编译器为了追求效率,不会自动识别形参有无值初始化,只会依次编译。
一、引言
任何国家的繁荣昌盛、落后衰亡都是由人民来推动历史发展,每个人都是历史发展的推动者。正是因为社会由千千万万的劳动人民组成的,社会才会进步。正是因为劳动人民的智慧,才造就富强繁荣的国家。我们基本上每天都在用的阿拉伯数字不也是劳动人民的智慧。阿拉伯数字最开始也不过是计数的一个工具,依靠劳动人民的智慧,发展成了+(加)、-(减)、×(乘)、÷(除)的运算符,让数学诞生了。计算机本身发明的初衷就是用来处理数据的,那些特殊的单位是否也可以有自己的运算符?譬如说我们用的最多的六十、十二进制可不可以设计运算符?
二、自定义类型的运算符重载的基本构成
在介绍自定义运算符重载之前,我想先介绍一下operator这个单词。operator意为操作员。在C++中操作自定义雷和对象的基本运算(所谓基本运算就是不能够自己创造运算符)。部分自定义运算符重载只能在全局定义。这些运算符我们一般都涉及不到,无须担心。另外有五个运算符无法重载(因为类和对象需要使用这些操作符调用函数和对象或是操作符在所有类域中不需要重载在C++中):点星(.*)、点(.)、sizeof()、域访问限定符、三木操作符之一的(判断式 ? 表达式一(判断式为真) : 表达式二(判断式为假)。
运算符定义非常简单:type(返回类型) operator (需要重载的运算符)(参数)即可。可以将运算符重载当成一种特殊的函数。
<code>type(返回类型) operator (需要重载的运算符)(参数)
{
}
三、运算符重载的实际运用
我们可以尝试制作一份电子日历(calendar)。日历当然要有保存功能、运算功能,运算符就可以用来运算,“巧妇难为无米之炊”我们先要定义一个类才能运算符重载。我们定义一个包含存储年、月、日三个变量的Date类。
// 使用这些函数时记得包含<stdio.h>或<iostream>文件
// 将日期写入文件
// 打开文件
FILE* pf1 = fopen("Date.txt","w");
// 判断文件是否成功打开
if (pf1 == NULL)
{
perror("fopen fail");
}
// 将值写入文件
fprintf(pf1,"%d\n",2024);
fprintf(pf1,"%2d\n", 1);
fprintf(pf1,"%2d\n", 1);
fclose(pf1);
// 从文件中读取日期
FILE* pf2 = fopen("Date.txt", "r");
if (p2 == NULL)
{
perror("fopen fail");
}
int year = 0, month = 0, day = 0;
// 读取文件并将读取到的值传入地址
fscanf(pf2,"%d",&year);
fscanf(pf2, "%2d",&month);
fscanf(pf2, "%2d",&day);
printf("%2d // %2d // %2d",year,month,day);
fclose(pf1);
上面的代码是关于文件操作的处理,上面的函数只要会用就行了,不需要完全搞清楚弄明白。因为只有将数据放入硬盘中才会长久保存,如果放入内存中,一旦程序结束数据就会被销毁。就像历史文化需要被记载好好传承。
三.一确定好每个月的天数
在完成运算符的开始我们应该先确定好每个月份的天数,这样才好进行加减乘除、前置后置加加、加等减等。考虑到确定好每个月份的天数调用比较频繁,所以将他放在类内定义(编译器默认该函数为inline内联函数(inline是对编译器的建议,编译器编译时也可以取消,建议函数不建立栈帧),适用于调用频繁、代码少、不递归的函数)
// 一三五七八十腊,三十一天永不差。0
// 平年二月二十八,闰年二月二十九。
// 闰年:四的倍数或者四百的倍数。
// 但不是一百和四共同的的倍数。
//
int GetMonthDay(int year, int month)
{
static int day[13] = {0,31,28,31,30,31,30,31,31,30,
31,30,31};
if (month == 2 &&
(year % 4 == 0 && year % 100 != 0
|| year % 400 == 0))
{
return 29;
}
return day[month];
}
三.二构造函数、拷贝构造函数、析构函数
紧接着我们来完成类和对象中的基本函数
// 全缺省的构造函数
//(注意除上面确定月份的天数的代码在类和对象中定义,
// 下面的代码都是申明(类域内)与定义(类域外)分开的)
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
// 拷贝构造函数
// d2(d1)
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 由于变量中没有向堆申请空间,或是带有其他资源,所以不需要写析构函数
// 析构函数
Date::~Date()
{
}
三.三 日期的基本运算
如果我们想重载出了一种运算符,可以用另一种相反的运算符加上非(!)来快速简单地完成,也可以用功能相近的运算符搞定。万事万物都存在一定的联系。譬如实现了大于等于(>=),那实现小于岂不是只要赋用(也可以说是函数嵌套调用)(>=)。加一个非(!)就搞定了。实现了加等(+=)可以搞定加(+);判断相等(==)反过来就可以判断不相等。以此类推,从而简化代码、减少内存。
// 第一种运算符重载的使用方法
Date d1,d2;
d1.operator = d2
// 第二种运算符重载的使用方法
d1 = d2;
我们首先实现的是等于(=)运算符的重载,因为拷贝构造函数也只是一种构造函数,也只能初始化时才能够调用,就像师傅领进门时会教你很多东西,后面逐渐要徒弟自己体会了。
// 等于运算符重载
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
我们简单来讲讲日期加等天数(+=)、日期减等天数(-=)、大于等于(>=)、小于等于(<=)、判断相等(==),剩下的只要按照逻辑来推理就八九不离十了。首先来说明一下前置加加和后置加加的区别:
//前置加加(另外尽量用引用返回减少不必要的拷贝,节约时间)
Daate& operator ++ (); // ++a(立马加一)
//后置++,用一个int充当标识符,区分后置与前置++
Date operator ++ (int); // a++(执行完当前代码再加一)
然后我们再来实现日期加等天数(+=)运算符,因为天数是日历的最小单位,所以我们令天数与天数相加,再判断该月是否有那么多天,没有就进位,减去该月的天数。
// 运算符 +=
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;
}
实现前置、后置++就简单多了,前置++直接+=1就行了,后置++可以拷贝一个原有值返回,再加加。加法也可以通过后置++的思路实现。
// 前置++
Date& Date::operator++()
{
Date tmp = *this;
*this += 1;
return tmp;
}
// 后置++
Date Date::operator++(int)
{
*this += 1;
return *this;
}
大于等于,反过来就是判断该值是否小于另一个值,是就是不大于等于,不是就是大于等于。
// 最高位先进行比较,等于就看下一位,有一位大于返回true
bool Date::operator >= (const Date& d)
{
if (_year < d._year)
{
return false;
}
if (_year == d._year)
{
if (_month < d._month)
{
return false;
}
if (_month == d._month)
{
if (_day < d._day)
{
return false;
}
}
}
return true;
}
判断相等就简单多了,直接判断三次是否相等即可。
// ==运算符重载
bool Date::operator==(const Date& d)
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。