1.赋值运算符重载
1.1运算符重载:
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator 操作符(参数列表)
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型参数
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this 5. .* :: sizeof ?: . ,注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
这里解释一下 .*
例子:实现 等于运算符重载(全局的)
重载成全局的无法访问私有成员变量
如何实现访问私有成员变量呢?
1.提供这些成员的get和set
2.友元函数
3.重载为成员函数(下面运用了方法3)
1.2赋值运算符重载:
赋值运算符重载格式:
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
检测是否自己给自己赋值
返回*this :要复合连续赋值的含义
拷贝构造与赋值重载区别:
实现赋值运算符重载:
1.3函数返回类型 传值与引用
从赋值运算符成员函数的函数返回值类型提出的问题
引子:左边是传值,右边是引用
理解过程:传值
引用:
针对这一代码,我们选值是正确的,func()函数中 d对象会提前析构,main()函数中 ref对象 接受的是已经被系统收回的空间(野指针),会用风险,比如后面加了 fx()函数 ,ret对象之前接受的值就会发生变化。
赋值运算符重载成员函数返回类型是 Date& ,原因有2个:1.返回的是 *this,而 this所指的对象 存在于main()函数中不会因为 赋值运算重载函数的销毁而析构 this所指的对象,并没有发生上面的情况;2.传值返回和引用返回都可以选,但是引用返回可以减少拷贝构造函数的次数,大大提高了效率,传值返回并不能减少,看下图相同的代码调用函数的次数。
1.4注意:
1.赋值运算符只能重载成类的成员函数不能重载成全局函数
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现
一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值
运算符重载只能是类的成员函数。
2.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
2.日期类的实现
2.1思路:
日期类 编译器默认生成的拷贝构造函数,赋值运算符函数,析构函数就可以;构造函数加个缺省值即可。
可以先实现 小于运算符重载函数 和 等于运算符重载函数 ,从而实现:小于等于,大于,大于等于,不等于 ,5个函数通过调用前面两个函数实现。
然后实现 加等运算符重载函数 和 减等运算符重载函数 ,从而实现:加,前置加价,后置加加减,前置减减,后置减减,6个函数通过调用前面两个函数实现。
这里,加等和减等函数都需要函数 GetMonthDay() 得到每个月的天数。
最后,求两个日期相隔多少天的函数,以及重载全局的插入流和提取流。
2.2头文件 Date.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
//友元函数声明
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1);
//括号外面修饰的是 *this
void Print()const;
//调用频繁
//直接定义在类里面,它默认是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 };
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
//检查日期是否非法
bool CheckDate();
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;
//d1 += 100
Date& operator+=(int day);
Date operator+(int day) const;
//d1 -= 100
Date& operator-=(int day);
Date operator-(int day) const;
//内置类型前置和后置区别不大 自定义建议前置的
//++d1 -> d1.operator++()
Date& operator++();
//d1++ -> d1.operator++(0)
//为了区分,构成重载,给后置++,强行增加了一个int形参
//这里参数仅仅是为了跟前置++构成重载区分
Date operator++(int);
Date& operator--();
Date operator--(int);
int operator-(const Date& d) const;
//流插入
//不建议,因为Date* this 占据了第一个参数位置,使用的 d<<cout 不符合习惯
//void operator<<(ostream& out); //他是禁止用拷贝函数的,所以加个引用&
private:
int _year;
int _month;
int _day;
};
//重载
//操作数的顺序本质是实参的顺序
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
2.3Date.cpp 函数的定义
#include"Date.h"
bool Date::CheckDate()
{
if (_month < 1 || _month > 12
|| _day < 1 || _day > GetMonthDay(_year, _month))
{
return false;
}
else
{
return true;
}
}
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout << "日期非法" << endl;
}
}
void Date::Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
// d1 < d2
bool Date::operator<(const Date& d) const
{
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;
}
// d1 <= d2
bool Date::operator<=(const Date& d) const
{
return *this < d || *this == d;
}
bool Date::operator>(const Date& d) const
{
return !(*this <= d);
}
bool Date::operator>=(const Date& d) const
{
return !(*this < d);
}
bool Date::operator==(const Date& d) const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator!=(const Date& d) const
{
return !(*this == d);
}
// d1 += 50
// d1 += -50
Date& Date::operator+=(int day)
{
if (day < 0)
{
return *this -= -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) const
{
Date tmp = *this;
tmp += day;
return tmp;
}
// d1 -= 100
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_month = 12;
_year--;
}
// 借上一个月的天数
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day) const
{
Date tmp = *this;
tmp -= day;
return tmp;
}
//++d1
Date& Date::operator++()
{
*this += 1;
return *this;
}
// d1++
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
// d1 - d2 得到多少天数
int Date::operator-(const Date& d) const
{
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;
}
//和习惯上有所不同
//void Date::operator<<(ostream& out)
//{
// out << _year << "年" << _month << "月" << _day << "日" << endl;
//}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
cout << "请依次输入年月日:>";
in >> d._year >> d._month >> d._day;
if (!d.CheckDate())
{
cout << "日期非法" << endl;
}
return in;
}
+= 和 + 两个函数实现思路类似,所以可以任意实现一个,另一个函数调用即可,这里建议实现+= ,+ 调用 += ,减少拷贝构造函数的次数,提高效率
关于日期类实现中前置++,后置++的问题两个点(前置--,后置-- 相同):
1.用参数个数不同区分,函数重载;
2.建议使用 前置++ 。
关于日期类实现中重载流插入,流提取的问题:
3.const成员
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
例子:
const 能加上就加上,使用范围更大。
权限平移,缩小都可以,放大不行。