C++【类和对象】(取地址运算符重载与实现Date类)
文章目录
- 取地址运算符重载
- const成员函数
- 取地址运算符重载
- Date类的实现
- Date.h
- Date.cpp
- 1.检查日期合法性
- 2. 构造函数/赋值运算符重载
- 3.得到某月的天数
- 4. Date类 +- 天数的操作
- 4.1 日期 += 天数
- 4.2 日期 + 天数
- 4.3 日期 -= 天数
- 4.4 日期 - 天数
- 5. Date的前后置++/--
- 5.1 前置++
- 5.2 后置++
- 5.3 前置--
- 5.4 后置--
- 6. Date类之间的比较
- 6.1 < 操作符
- 6.2 ==操作符
- 6.3 <=操作符
- 6.4 >操作符
- 6.5 >=操作符
- 6.6 !=操作符
- 7.日期-日期
- 8. Date的流插入与流提取
- 8.1流插入函数
- 8.2 流提取函数
- Test.cpp
- 结语
取地址运算符重载
const成员函数
- 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。
- const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。const修饰Date类的Print成员函数,Print隐含的this指针由
Date* const this
变成const Date* const this
还是拿Date类举例,如果我用const 修饰的对象来调用Print会发生报错,原因是Print隐含的this指针是Date* const this
,当我用const对象来调用Print会发生权限放大。
但是用const修饰成员函数,就可以解决问题,因为用const修饰后Print隐含的this指针由Date* const this
变成 const Date* const this
,就不会出现权限放大。
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
//cout << "这是构造函数" << endl;
_year = year;
_month = month;
_day = day;
}
//void Print(const Date* const this)
void Print() const
{
cout << _year << '/' << _month << '/' << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
取地址运算符重载
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器自动生成的就可以够我们用了,不需要去显示实现。除非一些很特殊的场景,比如我们不想让别⼈取到当前类对象的地址,就可以自己实现⼀份,胡乱返回⼀个地址。
Date* operator&()
{
//return nullptr;(胡乱给的地址)
return this;
}
const Date* operator&() const
{
//return nullptr;(胡乱给的地址)
return this;
}
Date类的实现
我们在实现一个任务的时候,可以让声明与定义分离,这是一个好习惯。
Date.h
#pragma once
#include<iostream>
using namespace std;
class Date
{
public:
//1.检查日期的合法性
bool CheckDate();
//2.构造函数与赋值运算符重载
Date(int year = 1, int month = 1, int day = 1);
Date& operator=(const Date& d);
bool LeapYear(int year)
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
return true;
}
return false;
}
//3.得到某月天数
int GetMonthDay(int year, int month)
{
static int month_day[] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && LeapYear(year))
{
return month_day[month] + 1;
}
return month_day[month];
}
//4. Date类 +- 天数的操作
Date operator+(const int day);
Date& operator+=(const int day);
Date operator-(const int day);
Date& operator-=(const int day);
//5. Date的前后置++/--
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
//6. Date类之间的比较
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);
void Print();
//7. 日期-日期(算相差多少天)
int operator-(const Date& d);
//8. 日期的流插入与流提取
friend ostream& operator<<(ostream& out, Date& d);
friend istream& operator>>(istream& in, Date& d);
private:
int _year;
int _month;
int _day;
};
Date.cpp
实现前要包头文件
#include"Date.h"
1.检查日期合法性
//检擦日期合法性
bool Date:: CheckDate()
{
if (_month <= 0 || _month >= 13 ||
_day > GetMonthDay(_year, _month) || _day <= 0)
{
return false;
}
return true;
}
2. 构造函数/赋值运算符重载
构造函数
Date::Date(int year, int month, int day)
{
//cout << "这是构造函数" << endl;
_year = year;
_month = month;
_day = day;
while (!CheckDate())
{
cout << "非法日期" << endl;
cout << *this;
cout << "请重新输入日期" << endl;
cin >> *this;
}
}
其实我们是不需要写拷贝构造和赋值运算符重载,因为Date类内部的成员对象并没有指向资源。
但是我们写赋值运算符重载是为了实现连续赋值。
Date& Date::operator=(const Date&d)
{
//如果不是自己给自己赋值
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//d1 = d2 应该返回前者(因为前者是被改变的) 所以要返回*this
return *this;
}
3.得到某月的天数
因为GetMonthDay
这个函数会被频繁大量的调用,所以我们放在类里面定义(类里面的函数默认inline
展开)
//判断闰年(其实这个函数没必要写,但我为了美观就写了该函数)
bool LeapYear(int year)
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
return true;
}
return false;
}
//得到某月的天数
int GetMonthDay(int year, int month)
{
//将month_day静态化,这样就不用反复的创建数组
static int month_day[] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && LeapYear(year))
{
return month_day[month] + 1;
}
return month_day[month];
}
4. Date类 ± 天数的操作
加减的是天数
4.1 日期 += 天数
加等使用的是进位思想,当这个月满了,向下个月借天数(同时月份也会+1)当月份大于12时,就将年份+1,同时月份到一月,直到日期为合法。
Date& Date::operator+=(const int day)
{
if (day < 0)
{
return *this -= (-day);
}
_day += day;
while (_day > GetMonthDay(_year,_month))
{
int tmp = GetMonthDay(_year, _month);
_day -= tmp;
++_month;
if (_month > 12)
{
_month = 1;
++_year;
}
}
return *this;
}
4.2 日期 + 天数
这里我们可以直接复用 日期+=天数 !
Date Date::operator+(const int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
4.3 日期 -= 天数
-=与+=的实现十分类似,只不过-=使用的是退位的思想,向前一个月借天数(月份-1),当月份小于1,年份-1,月份到12月,直到日期合法
Date& Date:: operator-=(const int day)
{
if (day < 0)
{
return *this += (-day);
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
int tmp = GetMonthDay(_year, _month);
_day += tmp;
}
4.4 日期 - 天数
和+一样,复用-=
Date Date::operator-(const int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
5. Date的前后置++/–
前置不需要加入int形参,后置需要加入int形参。
5.1 前置++
复用+=
Date& Date::operator++()
{
*this += 1;
return *this;
}
5.2 后置++
在参数位置上加一个int形参
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}
5.3 前置–
Date& Date::operator--()
{
*this -= 1;
return *this;
}
5.4 后置–
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
6. Date类之间的比较
完成 < + == 或者 > + ==其他的比较就可以复用了。
本文实现的是 < + ==
6.1 < 操作符
bool Date:: operator<(const Date& d)
{
if (_year > d._year)
{
return false;
}
else if (_year == d._year)
{
if (_month >= d._month)
{
return false;
}
else if (_month == d._month)
{
if (_day >= d._day)
{
return false;
}
}
}
return true;
}
6.2 ==操作符
bool Date::operator==(const Date& d)
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
6.3 <=操作符
bool Date::operator<=(const Date& d)
{
return (*this < d) || (*this == d);
}
6.4 >操作符
bool Date::operator>(const Date& d)
{
return !(*this <= d);
}
6.5 >=操作符
bool Date::operator>=(const Date& d)
{
return !(*this < d);
}
6.6 !=操作符
bool Date::operator!=(const Date& d)
{
return !(*this == d);
}
7.日期-日期
我们使用最简单的思路,取到区分较大和较小的日期,让较小的日期+1加到与较大的日期相等,同时记加了多少次,就得到了日期之间的天数。(cup的运行速度超级快,每秒上亿次,所以这点运算量对于cup来说轻轻松松)
//Date-Date
int Date::operator-(const Date& d)
{
int n = 0;
int flag = 1;
//假设法
Date min = *this;
Date max = d;
if (min > max)
{
min = d;
max = *this;
flag = -1;
}
//计算天数
while (min != max)
{
++min;
++n;
}
return n * flag;
}
8. Date的流插入与流提取
8.1流插入函数
ostream& operator<<(ostream& out, Date& d)
{
cout << d._year << '/' << d._month << '/' << d._day << endl;
return out;
}
注意,这里流插入和流提取都是全局函数,而非类的成员函数,其实我们在类里面也可以实现,但是会很怪。
由于成员函数的第一个参数默认是this,但运算符重载的规定是,当两个变量使用重载的运算符,会将左边的变量视为第一个参数,右边的变量视为第二个参数;这样的话,我们就不能写成cout << Date类实例化的对象
,而是要写成Date类实例化的对象 << cout
,cin
同理。
但是这样并不符合我们的习惯,所以我们设置成了全局函数,但是全局函数又有一个问题,就是Date类内部的成员变量是私有的,外部不能访问。
对此我们有以下几个方法:
- 将成员变量公有化(最最最不推荐的方法)
- 用get函数来得到成员变量
- 使用友元函数
- 放到类的内部(因参数问题不考虑)
这里我们使用的是友元函数(后面会详细讲解)。
8.2 流提取函数
istream& operator>>(istream& in, Date& d)
{
cin >> d._year >> d._month >> d._day;
while (!d.CheckDate())
{
cout << "非法日期" << endl;
cout << d;
cout << "请重新输入日期" << endl;
cin >> d;
}
return in;
}
Test.cpp
#include"Date.h"
void Test1()
{
Date d1;
Date d2(2026, 2, 28);
//Date d3(2029, 9, 33);
Date d3(2029, 9, 30);
d1.Print();
d2.Print();
d3.Print();
d3 = d2 = d1;
d1.Print();
d2.Print();
d3.Print();
}
void Test2()
{
Date d1(2026, 2, 28);
d1 += 10000;
d1.Print();
d1 -= 10000;
d1.Print();
Date d2 = d1 + 10000;
d2.Print();
Date d3 = d1 - 10000;
d3.Print();
}
void Test3()
{
Date d1(2026, 2, 28);
Date d2 = ++d1;
d2.Print();
Date d3 = d1++;
d3.Print();
Date d4 = --d1;
d4.Print();
Date d5 = d1--;
d5.Print();
}
void Test4()
{
Date d1(2024, 9, 28);
Date d2(2026, 2, 28);
cout << (d1 == d2) << endl;
cout << (d1 != d2) << endl;
cout << (d1 < d2) << endl;
cout << (d1 <= d2) << endl;
cout << (d1 > d2) << endl;
cout << (d1 >= d2) << endl;
}
void Test5()
{
Date d1(2024, 9, 28);
Date d2(2026, 2, 28);
cout << d1 - d2 << endl;
}
void Test6()
{
Date d1;
cin >> d1;
cout << d1;
}
int main()
{
//Test1();
//Test2();
//Test3();
//Test4();
//Test5();
Test6();
return 0;
}
结语
这次的分享就到这里结束了~
最后感谢您能阅读完此片文章~
如果您认为这篇文章对你有帮助的话,可以用你们的手点一个免费的赞并收藏起来哟~
如果有任何建议或纠正欢迎在评论区留言~
也可以前往我的主页看更多好文哦(点击此处跳转到主页)。