0


C++运算符的重载--日期类的实现

在学习中,用C++编程的都知道,C++提供了一个非常强大的操作符重载机制,利用操作符重载,我们可以为我们自定义的类增加更多非常有用的功能,本文就是结合自己所学过的知识,对日期类函数进行模拟实现.


1.Date.h

#include<stdio.h>
#include<iostream>
#include<assert.h>

using namespace std;
class Date
{
public:
    //构造函数
    Date(int year = 2008, int month = 1, int day = 1)
        :_year(year)
        ,_month(month)
        ,_day(day)
    {
        if (!IsInvalid())
        {
            assert(false);
        }
    }
    //拷贝构造
    //这里拷贝构造采用const修饰,const修饰拷贝构造的值不发生改变
    //避免发生一些操作上的失误
    Date(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    //析构函数
    ~Date()
    {

    }
    //判断输入日期是否符合规定
    bool IsInvalid();
    //打印函数
    void Show();
    //赋值运算符重载
    Date& 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);

    bool operator<(const Date& d);
    bool operator>(const Date& d);
    //
    Date operator+(int day);
    Date& operator+=(int day);
    //
    Date operator-(int day);
    Date& operator-=(int day);
    //前置++
    Date& operator++();
    //后置++
    Date operator++(int);
    //前置--
    Date& operator--();
    //后置--
    Date operator--(int);

    //计算两个日期之间的相差天数
    int operator-(const Date& d);
private:
    int _year;
    int _month;
    int _day;
};

在头文件的定义中,我们需要注意以下事项,第一,对于一个类来说,其要有构造函数,析构函数,拷贝构造,赋值构造等模块.在书写构造函数的时候,我们对类进行初始化列表进行初始化,这里一定要记住在初始化列表进行初始化的时候,只能进行一次初始化.

2.Date.cpp

#include"Date.h"
//进行日期类函数的实现

//判断是否是闰年
bool IsLeapYear(int year)
{
    if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
        return true;
    else
        return false;
}
//打印日期
void Date::Show()
{
    cout << _year << "-" << _month << "-" << _day << endl;
}

//获取对应月的天数
int GetMonthDay(int year, int month)
{
    int monthDay[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };
    if (month == 2 && IsLeapYear(year))
    {
        return 29;
    }
    return monthDay[month];

}
//判断天数是否符合规定
bool Date::IsInvalid()
{
    return _year >= 0 && _month >= 0 && _month <= 12
        && _day >= 0 && _day <= GetMonthDay(_year, _month);
}
//赋值运算符重载
Date& Date::operator=(const Date& d)
{
    if (this != &d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    return *this;
}
//运算符重载
bool Date::operator==(const Date& d)
{
    return this->_year == d._year
        && this->_month == d._month
        && this->_day == d._day;
}
//运算符重载
bool Date::operator!=(const Date& d)
{
    return !(*this == d);
}
//运算符重载
bool Date::operator>(const Date& d)
{
    if ((this->_year > d._year)
        || (this->_month > d._month && this->_year == d._year)
        || (this->_year == d._year && this->_month == d._month && this->_day > d._day))
    {
        return true;
    }
    else
        return false;
}
//'<'重载
bool Date::operator<(const Date& d)
{
    if ((this->_year < d._year)
        || (this->_month < d._month && this->_year == d._year)
        || (this->_year == d._year && this->_month == d._month && this->_day < d._day))
    {
        return true;
    }
    else
        return false;
}
//'>='重载
bool Date::operator>=(const Date& d)
{
    return (*this > d) || (*this == d);
}
//'<='重载
bool Date::operator<=(const Date& d)
{
    return (*this < d) || (*this == d);
}
//'+'加天数重载
Date Date::operator+(int day)
{
    Date tmp(*this);

    if (day < 0)
    {
        return *this - (-day);
    }
    //在临时变量里面加天数
    tmp._day += day;
    while (tmp._day > GetMonthDay(tmp._year, tmp._month))
    {
        tmp._day = tmp._day - GetMonthDay(tmp._year, tmp._month);

        if (tmp._month == 12)
        {
            tmp._year++;
            tmp._month = 1;
        }
        else
        {
            tmp._month++;
        }
    }
    return tmp;
}
//'-'号的重载
Date Date::operator-(int day)
{
    Date tmp(*this);
    if (day < 0)
    {
        return *this + (-day);
    }
    tmp._day -= day;
    while (tmp._day < 0)
    {
        //就要进行借位
        if (tmp._day < 0)
        {
            tmp._month = 12;
            tmp._year--;
        }
        else
        {
            tmp._month--;
        }
        int monthday = GetMonthDay(tmp._year, tmp._month);
        tmp._day += monthday;
    }
    return tmp;
}
//'+='的重载
Date& Date::operator+=(int day)
{
    *this = *this + day;
    return *this;
}
//'-='的重载
Date& Date::operator-=(int day)
{
    *this = *this - day;
    return *this;
}
//前置++
Date& Date::operator++()
{
    *this += 1;
    return *this;
}
//后置++
Date Date::operator++(int)
{
    Date tmp(*this);
    tmp = *this + 1;
    return tmp;
}
//前置--
Date& Date::operator--()
{
    *this -= 1;
    return *this;
}
//后置--
Date Date::operator--(int)
{
    Date tmp(*this);
    tmp = *this - 1;
    return tmp;
}
//计算两个日期之间的相差天数
int Date::operator-(const Date& d)
{
    //可以不用减  用相互之间的+进行操作
    int flag = 1;
    Date max(*this);
    Date min(d);
    //然后就是再进行判断一下
    /*if (max < min)
    {
        Date tmp = min;
        min = max;
        max = tmp;
        flag = -1;
    }*/
    if (*this < d)
    {
        min = *this;
        max = d;
        flag = -1;
    }
    //保证好大小之后
    int count = 0;
    while (min!= max)
    {
        ++min;
        ++count;
    }
    return count * flag;
}
void TestDate1()
{
    Date d9(2021, 3, 9);
    d9.Show();
    Date d10(2021, 3, 4);
    d10.Show();

    int day1 = d9 - d10;
    cout << day1 << endl;
}
int main()
{
    Date d1;
    d1.Show();
    Date d2(2022, 2, 24);
    d2.Show();
    Date d3;
    d3 = d2;
    d3.Show();

    
    //d3 = d2.operator+(10);
    //d3.Show();

    //d2.operator+=(20);
    //d2.Show();

    //Date d4 = d2.operator-(-30);
    //d4.Show();

    //d2.operator-=(30);
    //d2.Show();

    //Date d5 = d2++;
    //d5.Show();

    //++d2;
    /*Date d8(2023, 3, 4);
    Date d9(2021, 2, 3);
    d8.Show();
    d9.Show();
    int days = d8 - d9;
    cout << "相差天数:" << days << endl;*/
    TestDate1();

    return 0;
}

接下来,梳理一下整个逻辑线,函数所有的命名都采用驼峰法:

2.1 GetMonthDay()

因为不同的年份涉及到的月的天数不同,同样一年中的天数也会不同,这里进行定义一个数组monthDay[13]={ -1,31,28,31,30,31,30,31,31,30,31,30,31 };

这样定义的原因是因为:

  1. 月份的下标和实际月份相对应,在取对应的日期的时候更加方便;
  2. 定义成一个数组,更好操作;

但是很明显,闰年是要考虑的一个点,在C语言结构中我们判断过很多次了,同样我们也采用相应的方法进行判断,:

if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0))
return true;
else
return false;

2.2 IsInvalid()

判断输入的日期是否符合规定,通过调用上面定义的GetMonthDay进行判断,放在构造函数里面.

2.3 运算符重载=

在C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似.程序员可以根据自己的需求进行定义相关的类,写出相应的接口函数.

函数名字为:关键字operator后面接需要重载的运算符符号。

函数原型:返回值类型 operator操作符(参数列表)

这个功能,我认为在类中十分的重要,可以自己写运算符实现不同类的赋值拷贝等功能.

但是同样也有一些需要注意的点:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型或者枚举类型的操作数
  3. 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员的重载函数时,其形参看起来比操作数数目少个1成员函数,操作符有一个默认的形参this,限定为第一个形参
  5. .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载
Date& Date::operator=(const Date& d)
{
    if (this != &d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    return *this;
}

2.4 this指针

this指针是极其特殊的一个指针,那这个this指针存放在哪里呢?学习后发现,其实编译器在生成程序时加入了获取对象首地址的相关代码。并把获取的首地址存放在了寄存器ECX中(VC++编译器是放在ECX中,其它编译器有可能不同)。也就是成员函数的其它参数正常都是存放在栈中。而this指针参数则是存放在寄存器中。类的静态成员函数因为没有this指针这个参数,所以类的静态成员函数也就无法调用类的非静态成员变量.

2.5 运算符重载==

bool Date::operator==(const Date& d)
{
    return this->_year == d._year
        && this->_month == d._month
        && this->_day == d._day;
}

2.6 运算符重载!=

通过调用==,就可以实现相应的操作:

bool Date::operator!=(const Date& d)
{
    return !(*this == d);
}

不过这里有个bug,当初我刚开始想的时候,不知道怎么冒出一个想法:

bool Date::operator==(const Date& d)
{
    return this->_year != d._year
        && this->_month != d._month
        && this->_day != d._day;
}

想的就是我把==运算符的==全部替换成!=就可以,后面在进行其他运算符调用!=的时候才发现到bug,很明显这样不对的,这样增加了!=的一个满足条件:

  1. 对==运算符取反
  2. 将&&改成||,应该就可以满足对应的条件

2.7 运算符重载>

bool Date::operator>(const Date& d)
{
    if ((this->_year > d._year)
        || (this->_month > d._month && this->_year == d._year)
        || (this->_year == d._year && this->_month == d._month && this->_day > d._day))
    {
        return true;
    }
    else
        return false;
}

2.8 运算符重载<

bool Date::operator<(const Date& d)
{
    if ((this->_year < d._year)
        || (this->_month < d._month && this->_year == d._year)
        || (this->_year == d._year && this->_month == d._month && this->_day < d._day))
    {
        return true;
    }
    else
        return false;
}

2.9 运算符重载>

bool Date::operator>(const Date& d)
{
    if ((this->_year > d._year)
        || (this->_month > d._month && this->_year == d._year)
        || (this->_year == d._year && this->_month == d._month && this->_day > d._day))
    {
        return true;
    }
    else
        return false;
}

2.10 运算符重载>= <=

//'>='重载
bool Date::operator>=(const Date& d)
{
    return (*this > d) || (*this == d);
}
//'<='重载
bool Date::operator<=(const Date& d)
{
    return (*this < d) || (*this == d);
}

这个直接调用刚刚写的运算符重载就可以了.

2.11 运算符重载+

Date Date::operator+(int day)
{
    Date tmp(*this);

    if (day < 0)
    {
        return *this - (-day);
    }
    //在临时变量里面加天数
    tmp._day += day;
    while (tmp._day > GetMonthDay(tmp._year, tmp._month))
    {
        tmp._day = tmp._day - GetMonthDay(tmp._year, tmp._month);

        if (tmp._month == 12)
        {
            tmp._year++;
            tmp._month = 1;
        }
        else
        {
            tmp._month++;
        }
    }
    return tmp;
}

这个必须得说道说道,刚开始学的时候觉得这个不是很简单的嘛,完全没有难度,等到自己写的时候,大脑一片空白,要想学好编程,必须多学多练,将自己的想法写到程序上去,能正常运行才算是学会这种方法.

很明显,我们可以调用我们刚刚写的运算符重载函数,对应Date日期类加天数,月份不断地自加,要减去对应自己月份的日期,直到最后的天数小于对应的月份的天数,返回一个类.但是要记住:这里只是+,并不是+=返回的是一个类的临时拷贝,并没有改变对应的this指针指向的类.

2.12 运算符重载-

Date Date::operator-(int day)
{
    Date tmp(*this);
    if (day < 0)
    {
        return *this + (-day);
    }
    tmp._day -= day;
    while (tmp._day < 0)
    {
        //就要进行借位
        if (tmp._day < 0)
        {
            tmp._month = 12;
            tmp._year--;
        }
        else
        {
            tmp._month--;
        }
        int monthday = GetMonthDay(tmp._year, tmp._month);
        tmp._day += monthday;
    }
    return tmp;
}

2.13 运算符重载+= -=

//'+='的重载
Date& Date::operator+=(int day)
{
    *this = *this + day;
    return *this;
}
//'-='的重载
Date& Date::operator-=(int day)
{
    *this = *this - day;
    return *this;
}

2.14 运算符重载++

//前置++
Date& Date::operator++()
{
    *this += 1;
    return *this;
}
//后置++
Date Date::operator++(int)
{
    Date tmp(*this);
    tmp = *this + 1;
    return tmp;
}

这个前置++,必须好好说道说道,前置和后置的区别在于:开没开临时变量,有没有将值传给this指针,在C语言中我们也学习到了前置++和后置++的区别,在这里总结一下是指:前置++改变了this指针指向的类本身,而后置++我们则是新开了一个tmp的临时Date类变量,return这个临时的类,*this发生改变.

2.15 运算符重载--

//前置--
Date& Date::operator--()
{
    *this -= 1;
    return *this;
}
//后置--
Date Date::operator--(int)
{
    Date tmp(*this);
    tmp = *this - 1;
    return tmp;
}

2.16 计算两个日期类之间相差的天数

//计算两个日期之间的相差天数
int Date::operator-(const Date& d)
{
    //可以不用减  用相互之间的+进行操作
    int flag = 1;
    Date max(*this);
    Date min(d);
    //然后就是再进行判断一下
    /*if (max < min)
    {
        Date tmp = min;
        min = max;
        max = tmp;
        flag = -1;
    }*/
    if (*this < d)
    {
        min = *this;
        max = d;
        flag = -1;
    }
    //保证好大小之后
    int count = 0;
    while (min!= max)
    {
        ++min;
        ++count;
    }
    return count * flag;
}

这个代码刚开始出现一点bug,其实本质上不难,但是因为我的运算符重载!=写错了,我写成!=运算符重载的第二种方式,在进入while循环中发现:当this指向的日期类有一项相等时,!=就为false,整体就返回false,就跳出while循环了,所以那种方法不正确,要注意.

3.写在最后

在日常练习的时候,一定不要偷懒,要认认真真地将这些类自己重现一遍,出现bug就自己调试,可以学到很多!!!

标签: c++

本文转载自: https://blog.csdn.net/Hxbupaixu/article/details/125447774
版权归原作者 胡须不排序H 所有, 如有侵权,请联系我们删除。

“C++运算符的重载--日期类的实现”的评论:

还没有评论