学习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; 
};
相关推荐
fqbqrr2 小时前
2602c++,剪切板格式
c++
我材不敲代码2 小时前
Python爬虫介绍——简单了解一下爬虫
开发语言·爬虫·python
strings_lei2 小时前
AI 学习笔记 - AWS 相关服务
笔记·学习
Howrun7772 小时前
可调用对象
开发语言·c++
2 小时前
java关于引用
java·开发语言
小小码农Come on2 小时前
QT布局介绍
开发语言·qt
晚风吹长发2 小时前
初步了解Linux中的线程概率及线程控制
linux·运维·服务器·开发语言·c++·centos·线程
wdfk_prog2 小时前
[Linux]学习笔记系列 -- [drivers][gpio]gpio
linux·笔记·学习
诺狞猫2 小时前
思澈科技-SF32屏幕模组添加
科技·学习·思澈·sifli