学习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; 
};
相关推荐
weixin_44347851几秒前
Flutter学习之导航与路由
java·学习·flutter
咕泡科技1 分钟前
从0到1系统学习大模型:一份接地气的入门指南
人工智能·python·学习
wjs20245 分钟前
CSS 颜色
开发语言
无巧不成书02187 分钟前
Java数值字面量速查表
java·开发语言·python·开发者·字面量
小鸡吃米…7 分钟前
测试线程应用程序
开发语言·python
python开发笔记7 分钟前
python(79) 底层代码追踪工具
开发语言·python
kgduu8 分钟前
js之错误处理
开发语言·前端·javascript
Bert.Cai9 分钟前
Python函数的定义与调用
开发语言·python
美式请加冰11 分钟前
模拟的介绍和使用
java·开发语言·算法
无限进步_12 分钟前
深入解析vector:一个完整的C++动态数组实现
c语言·开发语言·c++·windows·git·github·visual studio