> 文档中心 > Let’s Make C++ Great Again——运算符重载练手程序

Let’s Make C++ Great Again——运算符重载练手程序

目录

  • 代码
    • Date.h
    • Date.cpp
    • test.cpp
    • 运行结果
  • 分析

代码

Date.h

#pragma once#include using namespace std;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();bool operator<(const Date& d);bool operator<=(const Date& d);bool operator>(const Date& d);bool operator>=(const Date& d);bool operator==(const Date& d);bool operator!=(const Date& d);Date& operator=(const Date& d);Date& operator+=(int d);Date operator+(int d);Date& operator-=(int d);Date operator-(int d);Date& operator++();Date operator++(int);Date& operator--();Date operator--(int);private:int is_leap(int _month);int getDays(int _year, int _month);int _year;int _month;int _day;};

Date.cpp

#include "Date.h"Date::Date(int _year, int _month, int _day) // 在声明中设置了参数默认值,定义就不能设置了,否则报出“重定义缺省参数”{this->_year = _year;this->_month = _month;this->_day = _day;}void Date::print(){cout << this->_year << '-' << this->_month << '-' << this->_day << endl;}int Date::is_leap(int _year){if (_year % 4 == 0 && _year % 100 != 0 || _year % 400 == 0){return 1;}return 0;}int Date::getDays(int _year, int _month){int months[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };return (_month == 2 ? months[_month] + is_leap(_year) : months[_month]);}bool Date::operator<(const Date& d){if (d._year != this->_year){return this->_year < d._year;}if (d._month != this->_month){return this->_month < d._month;}if (d._day != this->_day){return this->_day < d._day;}return false; // 补充代码}bool Date::operator<=(const Date& d){/*if (d._year != this->_year){return this->_year _month){return this->_month _day){return this->_day < d._day;}return true;*/return !(*this > d);}bool Date::operator>(const Date& d){if (d._year != this->_year){return this->_year > d._year;}if (d._month != this->_month){return this->_month > d._month;}if (d._day != this->_day){return this->_day > d._day;}return false; // 补充代码}bool Date::operator>=(const Date& d){/*if (d._year != this->_year){return this->_year _month){return this->_month _day){return this->_day < d._day;}return true;*/return !(*this < d);}bool Date::operator==(const Date& d){return d._year == this->_year&& d._month == this->_month&& d._day == this->_day;}bool Date::operator!=(const Date& d){return !(*this == d);}Date& Date::operator=(const Date& d){if (*this != d){this->_year = d._year;this->_month = d._month;this->_day = d._day;}return *this;}Date& Date::operator+=(int d) // 如果是我难以使用到这么简便的代码{if (d < 0) // 针对d为负数的情况{return (*this -= -d);}this->_day += d;while (this->_day >= getDays(this->_year, this->_month)){this->_day -= getDays(this->_year, this->_month);this->_month++;if (this->_month > 12){this->_month = 1;this->_year++;}}return *this;}Date Date::operator+(int d) // 加法不应该影响调用它的对象,需要创建一个临时变量,所以返回值类型不能是引用{if (d < 0){return (*this - -d);}Date tmp(*this);tmp._day += d;while (tmp._day >= getDays(tmp._year, tmp._month)){tmp._day -= getDays(tmp._year, tmp._month);tmp._month++;if (tmp._month > 12){tmp._month = 1;tmp._year++;}}return tmp;}Date& Date::operator-=(int d){if (d < 0) // 针对d为负数的情况{return (*this += -d);}this->_day -= d;//while (this->_day <= 0)//{//this->_day += getDays(this->_year, --this->_month); // 不能和+=相同//if (this->_month == 0)//{//this->_month = 12;//this->_year--;//}//}while (this->_day <= 0){if (--this->_month == 0){this->_month = 12;this->_year--;}this->_day += getDays(this->_year, this->_month);}return *this;}Date Date::operator-(int d){if (d < 0) // 针对d为负数的情况{return (*this + -d);}Date tmp(*this);tmp._day -= d;while (tmp._day <= 0){if (--tmp._month == 0){tmp._month = 12;tmp._year--;}tmp._day += getDays(tmp._year, tmp._month);}return tmp;}Date& Date::operator++(){return (*this += 1);}Date Date::operator++(int){Date tmp(*this);++(*this);return tmp;}Date& Date::operator--(){return (*this -= 1);}Date Date::operator--(int){Date tmp(*this);--(*this);return tmp;}

test.cpp

#include "Date.h"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;}int main(){Date d1;cin >> d1; // 2022 1 1Date d2 = d1;Date d3;d2 = d3 = d1;d1 -= 10;cout << d1 << endl;d1--;cout << d1 << endl;d1++;cout << d1 << endl;d1 += 10;cout << d1 << endl;d1 -= -100;cout << d1 << endl;d1 = d1 - -100;cout << d1 << endl;d1 += -100;cout << d1 << endl;d1 = d1 + -100;cout << d1 << endl;cout << (d1 > d2) << endl;cout << (d1 >= d2) << endl;cout << (d1 <= d2) << endl;cout << (d1 < d2) << endl;cout << (d1 == d2) << endl;cout << (d1 != d2) << endl;d3 = d1 + 10;cout << d3 << endl;d3 = d1 - 10;cout << d3 << endl;return 0;}

运行结果

Let’s Make C++ Great Again——运算符重载练手程序

分析

  • 对于构造函数中的缺省参数的设置:如果是声明与定义分开写的话,缺省参数只能设置在声明中,否则,报出重定义缺省参数

  • print函数其实可以被流插入运算符重载替换。
    流插入和流提取运算符重载,实现成全局函数更合适一些。
    因为实现成成员变量,函数参数列表处this会默认排在第一个,导致只有对象 << cout;对象 >> cin;时,才能正常进行输入输出,导致操作别扭。

    但是,将这两个函数设置成成员变量,也有个阻碍就是,全局函数不像成员函数一样,无法访问对象的私有或保护成员变量或函数。此时,将这两个运算符重载函数设置成友元函数即可

  • 返回值引用使用在了:=重载、+=重载、-=重载、前置++重载、后置–重载、流插入重载、流提取重载;

    原因:

    1. 允许链式操作。(尤其对于=、+=、-=)
      a = b = c实际情况是:a = (b = c)b = c的返回结果是b
      如果返回临时对象,a = b操作开始前,该临时对象的生命周期已经结束。无法完成链式赋值操作。但是我在vs2022中为value返回值时,可以正常链式赋值,可能时编译器进行了优化吧(留有疑问)。
    2. 返回引用,不需要调用构造函数和析构函数。提升了效率。
  • 在实现函数重载时,其实可以提高代码的复用。
    比如==就是!(!=)>=就是!<++就是+= 1等等诸如此类。

  • +=、-=、-、+运算符重载时的代码陷阱
    因为默认这些操作时的操作数都是正数,忽略了操作数为负数的情况。导致代码逻辑出现了bug。

    解决:使用if判断,让负数跳转到合适的函数中即可。

  • 赋值运算符重载时,防止自我赋值。
    防止自我赋值的意义是很大的,首先,为了效率毋庸置疑。其次,如果成员变量中存在属性在堆中,在进行赋值前,一定先进行地是,释放堆中资源,防止内存泄漏,此时进行自我赋值,则会导致数据丢失。

  • 将只在成员函数中使用的两个成员函数(is_leap()``getDays())的权限设置成私有,为了更好的封装性。

  • 前置++、-- 和 后置++、–
    实际上,运算符重载时的函数名只有:operator运算符这一种形式。那么如何对前置和后置运算符进行区分呢?C++设计者使用了整型占位符进行区分:即在后置运算符的形参列表中加入int
    前置运算符返回值可以是引用(返回的是操作数本身),而后置运算符返回值不可以是引用(返回的是重载函数中的局部变量)。这和前置后置操作的逻辑有关。