学习C++(6)日期类的实现+取地址运算符重载

1.日期类的实现

(1)全缺省构造函数、拷贝构造函数、赋值运算符重载函数、析构函数的实现
cpp 复制代码
public:
    Date(int year = 1900, int month = 1, int day = 1)
    {
        if (year < 1900|| month < 1 || month > 12
         || day < 1 || day > GetMonthDay(year, month))
        {
            cout << "非法日期" << endl;
        }
        _year = year;
        _month = month;
        _day = day;
    }
    Date(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }
    ~Date()
    {
        ;
    }

事实上,拷贝构造函数、赋值运算符重载函数和析构函数不需要自己去实现,因为Date类的成员变量全是内置类型,没有额外开辟空间,所以系统默认生成的函数就足够了。但这里的全缺省构造函数是根据日期类的特性写的,为了保证日期类的初始化合法,需要通过if语句加上一些限制。

(2)+=、+、-=、-(参数为天数)的实现

在实现这些函数之前,仔细思考实现的过程,会发现最大的难点其实是获取各年各月的天数,所以在此之前,需要先写一个GetMonthDay函数:

cpp 复制代码
 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; 
     }
     return monthDayArray[month];
 }

++static++ int monthDayArray[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };

这里加一个static把数组放到静态区,因为这个函数必然会被频繁地调用,这样每次调用就不用重新创建数组了
if (++month == 2 &&((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))++)

可见这个判断可以分为两个,一个是++month == 2++ ,另一个是++(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)++,明显前一个比后一个简单,所以把简单的放在前面,可以提高运行的效率

GetMonthDay不做声明和定义分离,因为这个函数必定会被频繁地调用,直接定义在类里面,则默认为内联函数,调用时直接展开,避免建立函数栈帧,提高效率。有了GetMonthDay函数,+=的实现就不难了,实现逻辑如下:

cpp 复制代码
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 > 12)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;
}

有了+=,+的实现其实可以通过直接复用+=函数来实现:

cpp 复制代码
Date Date::operator+(int day)
{
	Date d1 = *this;
	d1 += day;
	return d1;
}

这给我们提供了一个新思路,是否可以先实现+再复用+实现+=呢?也是可以的,代码如下:

cpp 复制代码
Date& Date::operator+=(int day)
{
	*this=*this+day;
	return *this;
}
Date Date::operator+(int day)
{
	Date d1 = *this;
	d1._day += day;
	while (d1._day > GetMonthDay(d1._year, d1._month))
	{
		d1._day -= GetMonthDay(d1._year, d1._month);
		d1._month++;
		if (d1._month > 12)
		{
			d1._year++;
			d1._month = 1;
		}
	}
	return d1;
}

*this++=++*this+day;

这里用到了赋值操作符重载函数
现在来讨论"1.先实现+=再复用+=实现+"与"2.先实现+再复用+实现+="这两种方法哪种更优:

观察代码可知第一种方法中+=没有拷贝,+拷贝了两次

第二种方法中+拷贝了两次,+=拷贝了三次

所以,总的来说1.先实现+=再复用+=实现+这种方法更好,效率更高。

再来看-、-=的实现,按照上面的分析,先来实现-=。

-=的实现逻辑和+=不同,代码如下:

cpp 复制代码
Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day <= 0)
	{
		if (_month == 1)
		{
			_year--;
			_month = 13;
		}
		_day += GetMonthDay(_year, --_month);
	}
	return *this;
}

-通过复用-=来实现,代码如下:

cpp 复制代码
Date Date::operator-(int day)
{
	Date d1=*this;
	return d1 -= day;
}

测试及结果:

(3)前置++、后置++、前置--、后置--的实现

前置++、后置++、前置--、后置--的实现并不难,只需要分清前置和后置的概念即可,前置则返回++后的类,后置则返回++前的类,但无论是后置还是前置,类都经过了+=1的过程;

cpp 复制代码
Date& Date::operator++()
{
	*this+=1;
	return *this;
}
Date Date::operator++(int)
{
	Date d1 = *this;
	*this += 1;
	return d1;
}
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
Date Date::operator--(int)
{
	Date d1 = *this;
	*this -= 1;
	return d1;
}

测试及结果:

(4)<、<=、>、>=、==、!=的实现

这一组函数也不需要每一个都实现,观察发现,只要实现了<和==,其他的函数都可以通过函数复用来实现。

先来看<的实现,两个日期之间作比较需要比较年、月、和日,如果逐个比较过程会非常繁琐,但如果只把<的情况筛选出来并返回true,其他的全部返回false,则会好写很多,代码如下:

cpp 复制代码
bool Date::operator<(const Date& d)
{
	if (_year < d._year)
	{
		return true;
	}
	else if (_year == d._year && _month < d._month)
	{
		return true;
	}
	else if (_year == d._year && _month == d._month && _day < d._day)
	{
		return true;
	}
	else
		return false;
}

其他函数的实现如下:

cpp 复制代码
bool Date::operator==(const Date& d)
{
	return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator<=(const Date& d)
{
	return *this < d || *this == d;
}
bool Date::operator>(const Date& d)
{
	return !(*this <= d);
}
bool Date::operator>=(const Date& d)
{
	return !(*this < d);
}
bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

测试及结果:

(5)-(参数为日期类)的实现

以下是一个较简单的实现方法,先判断两个日期类的大小,再让小的日期不断++,直到等于大的日期,在这个过程中计数,++了多少次就相隔了多少天。

cpp 复制代码
int Date::operator-(const Date& d)
{
	int flag = 1;
	Date max = *this;
	Date min = d;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int n = 0;
	while (min != max)
	{
		min++;
		n++;
	}
	return n * flag;
}

测试及结果:

2.取地址运算符重载

(1)const成员函数

将const修饰的成员函数称为const成员函数,const修饰成员函数放到成员函数参数列表的后面。

cpp 复制代码
void Print() const
{
    cout << _year << "-" << _month << "-" << _day << endl;
}

const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改,所以只有不需要改变自身的函数才能加const。

const 修饰Date类的Print成员函数后,Print隐含的this指针变为const Date* const this,缩小了权限,这个时候调用:

int main()

{

Date d1(2026, 1, 31);//对非const对象来说权限缩小了

d1.Print();

const Date d2(2026, 1, 31);//对const对象来说权限平移

d2.Print();

return 0;

}

(2)取地址运算符重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,一般这两个函数编译器自动生成的就足够我们用了,不需要去显式实现。

cpp 复制代码
class Date
{
public:
	Date* operator&()
	{
		return this;
		// return nullptr;
	}
	const Date* operator&()const
	{
		return this;
		// return nullptr;
	}
private:
	int _year; 
	int _month; 
	int _day; 
};
相关推荐
wefg11 分钟前
【算法】倍增思想(快速幂)
数据结构·c++·算法
无名之逆8 分钟前
你可能不需要WebSocket-服务器发送事件的简单力量
java·开发语言·前端·后端·计算机·rust·编程
Remember_9938 分钟前
一文吃透Java WebSocket:原理、实现与核心特性解析
java·开发语言·网络·websocket·网络协议·http·p2p
锅包一切19 分钟前
一、C++ 发展与程序创建
开发语言·c++·后端·学习·编程
一株菌子1 小时前
10.12 总结
开发语言·python
power 雀儿1 小时前
LibTorch激活函数&LayerNorm归一化
c++·人工智能
枷锁—sha1 小时前
【CTFshow-pwn系列】03_栈溢出【pwn 051】详解:C++字符串替换引发的血案与 Ret2Text
开发语言·网络·c++·笔记·安全·网络安全
沙白猿1 小时前
【TJXT】Day3
java·开发语言
一个处女座的程序猿O(∩_∩)O1 小时前
Python面向对象的封装特性详解
开发语言·python
zhaoyin19941 小时前
python基础
开发语言·python