文章目录
- 一.运算符重载
- 1.运算符重载的概念
- 2.实现Date类
- (1)> < >= <= != 重载
- (2)+= -= + - 重载
- (3)前置++与后置++重载
- (4)日期-日期的实现
- (5)<< 与 >>重载
- 2.默认成员函数——赋值运算符重载
- 二.默认成员函数——取地址操作符重载
- #. 补充知识点:const成员
前言:
本章将通过日期类的实现,深入学习运算符重载的实现方法。本章将完成6个默认成员函数中剩余3个——赋值运算符重载与取地址操作符重载的学习。
一.运算符重载
1.运算符重载的概念
C++
为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
- 函数名:关键字
operator
后面接需要重载的运算符符号。 - 函数原型:
返回值类型 operator操作符(参数列表)
bool operator==(Date d1,Date d2);
需要注意的是:
- 不能通过连接其他符号来创建新的操作符:比如
operator@
- 重载操作符必须有一个类类型参数
- 用于内置类型的运算符,其含义不能改变,例如:内置的整型
+
,不 能改变其含义 - 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的
this
.*
::
sizeof
?:
.
注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
2.实现Date类
定义一个Date类:
class Date
{
public:
//构造函数
Date(int year = 0, int month = 0, int day = 0)
{
//判断日期是否合法
//GetMonthDay()获取这个月的天数
if (month > 0 && month < 13 &&
(day > 0 && day <= GetMonthDay(year, month)))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "日期非法" << endl;
}
}
private:
int _year;//年
int _month;//月
int _day;//日
};
函数接口:
// 类里面短小函数,适合做内联的函数,直接是在类里面定义的
class Date
{
// 友元函数声明
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
// 构造
Date(int year = 0, int month = 0, int day = 0);
void Print() const;
// 当月的天数
int GetMonthDay(int year, int month) const;
// 重载日期类与日期类运算==, !=, <, <=, >, >=
bool operator==(const Date& d) const;
bool operator!=(const Date& d) const;
bool operator<(const Date& d) const;
bool operator<=(const Date& d) const;
bool operator>(const Date& d) const;
bool operator>=(const Date& d) const;
// 重载日期类与天运算+=, +, -=, -,
Date& operator+=(int day);
Date operator+(int day) const;
Date& operator-=(int day);
Date operator-(int day) const;
// 重载日期类与日期类运算-
int operator-(const Date& d) const;
// 赋值运算符重载
Date& operator=(const Date& d);
// 重载日期类前置++--,后置++--
Date& operator++();
// int参数 仅仅是为了占位,跟前置重载区分
Date operator++(int);
Date& operator--();
Date operator--(int);
//取地址重载
Date* operator&();
const Date* operator&() const;
private:
int _year;
int _month;
int _day;
};
inline ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日";
return out;
}
inline istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
在实现运算符重载的时候,有一点需要特别注意:
二元运算符的重载函数的参数有两个,规定第一个参数为左操作数,第二个参数为右操作数。
在前面章节我们讲过成员函数的特性,成员函数有一个自带的参数this
,类型为类类型。所以我们可以省略第一个参数,只写第二个参数。
所以:
(1)> < >= <= != 重载
先实现两个运算符重载函数,其它的就可以复用已经实现好的运算符。
class Date
{
public:
//构造函数
//...
bool operator==(const Date& d)
{
return (_year == d._year) && (_month == d._month) && (_day == d._day);
}
bool operator<(const Date& d)
{
return _year < d._year
|| (_year == d._year && _month < d._month)
|| (_year == d._year && _month == d._month && _day < d._day);
}
bool operator<=(const Date& d)
{
//函数的复用
return *this < d || *this == d;
}
bool operator>(const Date& d)
{
//函数的复用
return !(*this <= d);
}
bool operator>=(const Date& d)
{
//函数的复用
return !(*this < d);
}
bool operator!=(const Date& d)
{
//函数的复用
return !(*this == d);
}
//...
};
(2)+= -= + - 重载
注意:下面四个运算符重载的右操作数都为day
天数
class Date
{
public:
//...
//获取当月的天数
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
int monthArray[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;
}
else
{
return monthArray[month];
}
}
//+= 返回自身的引用,减少拷贝
Date& operator+=(int day)
{
//判断是否加了负数
if (day < 0)
{
//复用
*this -= -day;
return *this;
}
_day += day;
while (_day > GetMonthDay(_year, _month));
{
_day -= GetMonthDay(_year, _month);
//进位
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
//-= 返回自身的引用,减少拷贝
Date& operator-=(int day)
{
//判断是否减了一个负数
if (day < 0)
{
//复用
*this += -day;
return *this;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date operator+(int day)
{
//拷贝构造
//因为加不改变自身的值,所以创建临时对象
Date tmp(*this);
//复用
tmp += day;
return tmp;
}
Date operator-(int day)
{
Date tmp(*this);
tmp -= day;
return tmp;
}
//...
};
(3)前置++与后置++重载
前置++
与后置++
都是一元运算符,这二者的区别是:
- 前置
++
:先++
再使用,返回++
之后的数 - 后置
++
:先使用再++
,返回++
之前的数
为了能在重载的时候做出区分,·C++·规定:
- 后置
++
重载时多增加一个int
类型的参数,但调用时该参数不用传递,编译器会自动传递。
class Date
{
public:
//...
//前置++
Date& operator++()
{
*this += 1;
return *this;
}
//后置++
// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,
// 故需在实现时需要先将this保存一份,然后给this + 1
// 而temp是临时对象,因此只能以值的方式返回,不能返回引用
Date operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
//前置--
Date& operator--()
{
*this -= 1;
return *this;
}
//后置--
Date operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
//...
};
(4)日期-日期的实现
日期+日期没有意义,但是日期-日期有意义,日期-日期代表两日期相距多少天
class Date
{
//...
int operator-(const Date& d)
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}
//...
}
(5)<< 与 >>重载
Date d1(2023,5,1);
cout<<d1;
<<
与>>
是二元操作符,上文提到二元操作符第一个参数为左操作符,第二个参数为右操作符- 因为类中成员函数第一个参数为
this
,所以左操作数就成了对象,右操作数变成了cout
,这就成了d1<<cout
,与平常使用的C++
语法习惯不符,所以我们不能将<<
与>>
写到类的成员函数中,而是重载在类外面 - 但是类外的函数无法访问类的私有函数,所以我们将重载函数设置为友元函数来实现。
class Date
{
//...
//申明友元函数
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
//...
}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日";
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
2.默认成员函数——赋值运算符重载
与之前的构造函数与析构函数等默认成员函数相同,赋值运算符重载也是类的6个默认成员函数之一。
赋值运算符重载具有以下特性:
- 赋值运算符重载格式:
参数类型:const &T
,参数引用可以提高传参效率
返回值类型:T&
,返回值引用可以提高返回的效率,有返回值目的是为了支持连续赋值,检测是否给自己连续赋值
返回*this
:要复合连续赋值的定义 - 赋值运算符只能重载成类的成员函数不能重载成全局函数;
class Date
{
//...
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
//...
}
二.默认成员函数——取地址操作符重载
6个默认成员函数只剩两个——取地址重载与const
取地址重载。但是,这两个函数实在没有实现的必要,因为我们自己实现与编译器自动实现出来的效果是一样的。
class Date
{
//...
Date* operator&()
{
return this;
}
const Date* operator&()const
{
return this;
}
//...
};
#. 补充知识点:const成员
将const
修饰的成员函数称之为const
成员函数,const
修饰类成员函数,实际修饰的该成员函数隐含的this
指针,表明在该成员函数中不能对类的任何成员进行修改。
例如:
class Date
{
public:
//...
void print()
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
private:
int _year;
int _month;
int _day;
}
void Test3()
{
Date d1(2023, 4, 1);
d1.print();
const Date d2(2022, 3, 1);
d2.print();
}
运行结果:
这是因为权限放大了:我们不能将const Date &d2
传递给形参Date* this
正确写法为:
void print() const
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
无法显示的修饰隐含参数*this
,所以在函数后面加上const
修饰。这样做适合不在成员函数内修改成员变量的函数,对无const
修饰的类同样适用。
本文到此结束,码文不易,还请多多支持哦!