4.这两种方案哪一个更好呢?
对于+复用+=来说
+=:拷贝了0次
+:拷贝了2次:分别是:
对于+=复用+来说
+拷贝了2次:分别是:
+=拷贝了3次:分别是:
因此使用+复用+=更好
同理,使用-复用-=更好
5.一个"坑点"
在+=复用+的版本中
但是当我们写好了这个复用函数后会发现编译器报错
这是为什么呢?
是因为我们所写的赋值运算符重载函数
Date& operator=(Date& d);
这个参数列表中没有加上const修饰
而*this=*this+day;
这个表达式的右值返回的是一个临时变量,而临时变量具有常性
也就可以认为这个临时变量具有const修饰
然后继续调用赋值运算符重载函数,可是赋值运算符重载函数的参数并没有const修饰,因此出现了权限的放大问题,进而报错
同理,如果我们这样调用拷贝构造函数也是如此
Date d((*this) + day);
当我们加上const之后就没事了
五.++运算符重载
1.前置++的运算符重载
我们可以直接复用+=运算符
//前置++:拷贝0次 Date& operator++() { *this += 1; return *this; }
这里注意:
因为+=已经考虑了不同月份的具体天数不同,因此我们这里就不需要去管不同月份天数的影响了
2.后置++
C++创始人规定
前置++这样去定义:
Date& operator++()
后置++这样去定义:
Date operator++(int)
这里就印证了我们之前在学习缺省参数的时候学习到的一点:
编译器允许函数的参数列表中的参数只写类型,不写名称
这个参数设计的目的就是为了区分前置++和后置++
那么为什么要设计为int呢?
没有为什么,C++创始人就是这么规定的
六.日期跟天数相减的运算符重载
1.怎么相减呢?
注意这里的红字
就是先
_day-=day;
然后向月份借天数,注意:向前一个月份借天数
直到_day>0为止
注意:
如果有人就是想要-=一个负数
那就相当于+=这个负数的相反数
因此我们在这里加了一个if先去判断
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& operator+=(int day) { //加一个负数==减去这个负数的相反数 if (day < 0) { return (*this) -= (-day); } _day += day; while (_day > GetMonthDay(_year, _month)) { _day -= GetMonthDay(_year, _month); _month++; if (_month == 13) { _month = 1; _year++; } } return *this; }
2.-复用-=
Date operator-(int day) { Date d(*this); d -= day; return d; }
七.日期-日期计算天数
还有最后一个需求
我们想要去计算两个日期之间相差多少天
因为每个月的天数都不相同,而且还有闰年的干扰
所以只能这么去计算相差多少天
int operator-(Date& d) { Date max = *this; Date min = d; int flag = 1;//假设:*this大于d,左大于右 左-右 //假设错误 if (min > max) { max = d; min = *this; flag = -1;//右大于左 } int day = 0; while (min < max) { //++min;//++min是代码实现是调用+=的,那我直接调用+=不好吗,更快 min += 1; day++; } return flag * day; }
我们在网上找一下日期转换工具测试一下
八.流插入和流提取运算符重载
1.流插入运算符重载的引出
我们之前提到过C++创始人对于C语言中的左移运算符<<和右移运算符>>
又分别添加了一种语法
规定<<不仅可以表示左移运算符,也可以表示流插入运算符
/>>不仅可以表示右移运算符,也可以表示流提取运算符
对于内置类型:
我们可以这样去打印:
说明流插入是可以自动识别类型的
那么我们可不可以这样做呢?
当然是做不了的
为什么呢?
因为当我们没有重载流插入运算符时,编译器是无法得知我们想要怎样打印这个自定义类型的
因此流插入运算符是无法识别自定义类型的
那么为什么流插入运算符能够自动识别内置类型呢?
其实是因为C++库中已经实现好了这些自定义类型的流插入运算符重载函数
而cout和cin的类型分别是:
cout的类型是:ostream
cin的类型是:istream
我们可以这么理解:
平常我们使用cout来打印数据时
例如:
cout<<100;
本质是调用了<<的一个重载版本:
我们可以理解为:cout是这样打印的
在这个运算符重载的类当中
cout被隐含的this指针传过去,而另一个参数就是我们要打印的这个100
这个<<的重载函数是这样的: ostream& operator<<(int val); cout.operator<<(100);
2.流插入运算符重载的类内版本
于是我们就可以写出<<的重载版本
void Date::operator<<(ostream& out) { out << _year << " " << _month << " " << _day << endl; }
这里我们用out作为参数,跟cout的名字区分一下
其实当我们调用cout<
我们会把cout传入并用out来接收,而且这个out的类型是一个引用,也就是说这个out就是cout的一个别名
然后我们去打印,但是发现了一个"奇怪"的现象
3.类内版本的局限性
使用cout<
但是更奇怪的现象是:
使用d1<
这是为什么呢?
我们可以这样理解:
4.友元
实现一下:
void operator<<(ostream& out, const Date& d) { out << d._year << " " << d._month << " " << d._day << endl; }
此时就可以正常运行了
因此我们只需要这样做
就可以正常运行了
5.想要连续进行流插入怎么办呢?
下面的问题是<<流插入运算符是支持连续进行的啊
那么我们可不可以也这样呢?
报错
为什么呢?
因为我们刚才实现的是
上面的分析过程就有点类似于物理中的整体+隔离法进行受力分析了
下面我们来实现一下:
ostream& operator<<(ostream& out, const Date& d) { out << d._year << " " << d._month << " " << d._day << endl; return out; }
6.流提取>>运算符重载
类似于流插入<<,流提取也可以按照同样的思路去重载
istream& operator>>(istream& in, Date& d) { in >> d._year >> d._month >> d._day; return in; }
7.C++为什么要设计流插入和流提取呢?
九.const成员函数
1.为什么要有const成员函数
有些情况下,我们需要定义出const修饰的类型的对象
而这时,因为我们的成员函数中的this指针默认是没有const去修饰的
那么这种情况下就会出现这种错误
怎么办呢?
我们无法显式定义this指针,那么就无法直接在this指针的类型前面加上const修饰
于是C++创始人规定:
我们可以这样去给一个成员函数的this指针加上const修饰
void Print() const { cout << _year << " " << _month << " " << _day << endl; }
就是在函数的()后面加上const
2.成员函数没有加const的意想不到的错误
下面就是一个典型的错误示范:
而当我们给>运算符函数加上const修饰之后
正常运行
3.const成员函数定义的原则
4.几个小问题
下面我们来验证一下:
第4条
第3条:
十.取地址运算符重载
这是最后两个类的默认成员函数
既然是类的默认成员函数,那么就说明:如果我们没有定义取地址运算符重载函数,那么编译器会生成默认的取地址运算符重载函数
它们的具体实现如下:
Date* operator&() { return this; } const Date* operator&() const { return this; }
这两个运算符一般不需要重载,使用编译器生成的即可,只有特殊情况,才需要重载,比如不想让别人得到正确的地址
Date* operator&() { return nullptr; //或者直接返回一个错误地址 return (Date*)0x11223344; }
十一.具体的解决方案(日期类的完善)
我们将Date类的声明跟定义分离
结果发现:
1.缺省参数的一个常见错误
为什么会报错呢?
这个原因我们在学习缺省参数的时候提到过,
也就是:缺省参数的缺省值不能同时在声明和定义中都有
规定要在声明中给出,定义中不要给缺省值
这样就可以了
2.Date.h
#pragma once #include <iostream> using namespace std; #include <assert.h> class Date { public: void Print() const; Date(int year = 1, int month = 1, int day = 1); Date(const Date& d1); Date& operator=(const Date& d); 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; //返回year年month月有多少天 int GetMonthDay(int year, int month); //上面这个+复用+=更好 //拷贝0次 Date& operator+=(int day); //+复用+= //拷贝2次 Date operator+(int day) const; //前置++:拷贝0次 Date& operator++(); //后置++,为什么是int呢?,C++创始人定的语法,没有为什么 //拷贝2次 Date operator++(int); //前置++拷贝0次,后置++拷贝2次,因此前置++更好 Date& operator-=(int day); Date operator-(int day) const; int operator-(const Date& d) const; friend ostream& operator<<(ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d); Date* operator&() { return this; } const Date* operator&() const { return this; } private: int _year; int _month; int _day; };
3.Date.cpp
#include "Date.h" void Date::Print() const { cout << _year << " " << _month << " " << _day << endl; } Date::Date(int year, int month, int day) { //对传入的年份进行检查 if ((year < 0) || (month < 1 || month>12) || (day<1 || day>GetMonthDay(year, month))) { //assert(false);//粗暴一点的方法 Print();//本质是:this->Print(); cout << "输入的日期为非法日期" << endl; exit(-1); } _year = year; _month = month; _day = day; } Date::Date(const Date& d1) { //对传入的年份进行检查 if ((d1._year < 0) || (d1._month < 1 || d1._month>12) || (d1._day<1 || d1._day>GetMonthDay(d1._year, d1._month))) { //assert(false);//粗暴一点的方法 Print();//本质是:this->Print(); cout << "拷贝对象的日期为非法日期" << endl; cout << "无法进行拷贝" << endl; exit(-1); } _year = d1._year; _month = d1._month; _day = d1._day; } Date& Date::operator=(const Date& d) { //对传入的日期进行检查 if ((d._year < 0) || (d._month < 1 || d._month>12) || (d._day<1 || d._day>GetMonthDay(d._year, d._month))) { //assert(false);//粗暴一点的方法 cout << "拷贝对象的日期为非法日期: "; cout << "无法进行拷贝" << endl; exit(-1); } _year = d._year; _month = d._month; _day = d._day; return *this; } 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); } bool Date::operator>(const Date& d) const { 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; } bool Date::operator<=(const Date& d) const { return !((*this) > d); } bool Date::operator>=(const Date& d) const { return (*this) > d || (*this) == d; } bool Date::operator<(const Date& d) const { return !((*this) >= d); } //返回year年month月有多少天 int Date::GetMonthDay(int year, int month) { 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))) { MonthArray[month]++; } return MonthArray[month]; } //上面这个+复用+=更好 //拷贝0次 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) { _month = 1; _year++; } } return *this; } //+复用+= //拷贝2次 Date Date::operator+(int day) const { Date d1(*this); d1 += day; return d1; } //前置++:拷贝0次 Date& Date::operator++() { *this += 1; return *this; } //后置++,为什么是int呢?,C++创始人定的语法,没有为什么 //拷贝2次 Date Date::operator++(int) { Date d(*this); //+=已经考虑了不同月份日期不同 *this += 1; return d; } //前置++拷贝0次,后置++拷贝2次,因此前置++更好 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 d(*this); d -= day; return d; } int Date::operator-(const Date& d) const { Date max = *this; Date min = d; int flag = 1;//假设:*this大于d,左大于右 左-右 //假设错误 if (min > max) { max = d; min = *this; flag = -1;//右大于左 } int day = 0; while (min < max) { //++min;//++min是代码实现是调用+=的,那我直接调用+=不好吗,更快 min += 1; day++; } return flag * day; } ostream& operator<<(ostream& out, const Date& d) { out << d._year << " " << d._month << " " << d._day << endl; return out; } istream& operator>>(istream& in, Date& d) { in >> d._year >> d._month >> d._day; return in; }
以上就是
C++类和对象中:运算符重载+const成员函数+日期类的完善
的全部内容,
希望能对大家有所帮助!