📌 相关专栏
-
【C++ 专栏】
📌 相关文章推荐
很高兴你点开这篇文章✨
这里会持续更新我喜欢的内容,关注我,一起慢慢变好呀
👍 点赞 ⭐ 收藏 💬 评论
文章目录
- 前言
- 一、构造函数
-
- [1.1 什么是构造函数](#1.1 什么是构造函数)
- [1.2 声明与定义分离时缺省参数的位置](#1.2 声明与定义分离时缺省参数的位置)
- 二、辅助函数:获取月份天数
- 三、运算符重载
-
- [3.1 什么是运算符重载](#3.1 什么是运算符重载)
- [3.2 日期 += 天数](#3.2 日期 += 天数)
- [3.3 日期 + 天数 vs 日期 += 天数](#3.3 日期 + 天数 vs 日期 += 天数)
- [复用原则:用 += 实现 +](#复用原则:用 += 实现 +)
- [3.4 前置++ vs 后置++](#3.4 前置++ vs 后置++)
- [3.5 比较运算符](#3.5 比较运算符)
- [3.6 日期 - 日期(计算天数差)](#3.6 日期 - 日期(计算天数差))
- 四、流插入(cout)与流提取(cin)重载
-
- [4.1 问题:为什么不能写在类里面?](#4.1 问题:为什么不能写在类里面?)
- [4.2 友元函数](#4.2 友元函数)
- [4.3 实现流插入和流提取](#4.3 实现流插入和流提取)
- 五、知识点汇总
- 六、本文的所有代码
-
- [🐾 </h3>Date.cpp](#🐾 Date.cpp)
- [🐾 </h3>Date.h](#🐾 Date.h)
- [🐾 </h3>Test.cpp](#🐾 Test.cpp)
前言
在前一篇文章中,我们学习了类的基础知识:访问限定符、类域、this指针等。这一篇,我们要真正动手写一个完整的类------Date类。
💡接下来的这篇文章,我们将一起学到:
- 构造函数如何初始化对象
- 如何让日期支持 +、-、++、-- 等运算符
- 前置++和后置++有什么区别,如何重载
- 如何让cout << d1 直接打印日期
- 如何复用写好的运算符,避免重复代码
让我们从一个简单的日期类开始。
🐶 🐾 ✨ 🐾 🐶
一、构造函数
1.1 什么是构造函数
构造函数是对象实例化时自动调用的函数,用于初始化对象。
🐾 特点:
- 函数名与类名相同
- 没有返回值
- 可以带参数(支持重载和缺省参数)
- 对象创建时自动执行
cpp
class Date
{
public:
// 构造函数(全缺省)
Date(int year = 2005, int month = 2, int day = 8)
{
_year = year;
_month = month;
_day = day;
// 检查日期是否合法
if (!CheckDate())
{
cout << "日期非法:";
Print();
}
}
private:
int _year;
int _month;
int _day;
};
// 使用方式
Date d1; // 使用缺省值 2005/2/8
Date d2(2026, 3, 23); // 使用传入的值
1.2 声明与定义分离时缺省参数的位置
重要规则:缺省参数只能写在声明中,不能出现在定义中。
cpp
// Date.h
class Date
{
public:
Date(int year = 2005, int month = 2, int day = 8); // ✅ 声明写缺省
};
// Date.cpp
Date::Date(int year, int month, int day) // ❌ 定义不能再写缺省
{
_year = year;
_month = month;
_day = day;
}
🐶 🐾 ✨ 🐾 🐶
二、辅助函数:获取月份天数
cpp
//默认是内联inline函数,则不需要为了调用函数而建立栈帧
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
// static修饰,数组只初始化一次,避免重复创建,创建一个月份数组来储存每月对应的天数
static int monthDayArr[13] = { -1, 31, 28, 31, 30, 31, 31, 30, 31, 30, 31, 30, 31 };
// 闰年2月特殊处理
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29; //对应闰年二月的29天
}
return monthDayArr[month];
}
注意:
- static 数组只初始化一次,提高效率
- 下标从1开始对应1月,0位置用
-1占位 - 闰年判断:能被4整除且不能被100整除,或能被400整除
cpp
static int monthDayArr[13] = { -1,31,28,31,30,31,31,30,31,30,31,30,31 };
//第一个-1是为了补上0这个位置,保证每一个月对应相应的天数
//月份数组元素:-1 31 28 31 30 31 31 30 31 30 31 30 31
//月份数组下标:0 1 2 3 4 5 6 7 8 9 10 11 12
🐶 🐾 ✨ 🐾 🐶
三、运算符重载
3.1 什么是运算符重载
C++允许我们重新定义运算符的行为,让自定义类型也能使用 +、-、++ 等运算符。
- 🐾 语法: 返回值 operator运算符(参数列表)
3.2 日期 += 天数
cpp
//日期+=天数
Date& Date::operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
}
return *this; //首先引用返回目的是避免传参而减少拷贝构造的次数提高效率
//其次this指针是类的地址,我们要返回类,所以对this的引用
}
3.3 日期 + 天数 vs 日期 += 天数
| 运算符 | 是否修改原对象 | 返回值类型 | 典型用法 |
|---|---|---|---|
+= |
是 | 引用 Date& | d1 += 100 |
+ |
否 | Date 值 | Date d2 = d1 + 100 |
复用原则:用 += 实现 +
cpp
Date Date::operator+(int day)
{
Date tmp = *this; // 拷贝一份
tmp += day; // 复用 +=
return tmp; // 返回拷贝(tmp是局部变量)
}
3.4 前置++ vs 后置++
| 类型 | 写法 | 返回值 | 实现 |
|---|---|---|---|
前置++ |
++d1 | 引用 Date& | 先自增,再返回自己 |
后置++ |
d1++ | Date 值 | 先保存副本,再自增,返回副本 |
cpp
// 前置++(返回引用)
Date& Date::operator++()
{
*this += 1;
return *this;
}
// 后置++(返回副本,int参数是占位符)
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}
3.5 比较运算符
cpp
bool Date::operator<(const Date& d)
{
if (_year < d._year) return true;
if (_year == d._year && _month < d._month) return true;
if (_year == d._year && _month == d._month && _day < d._day) return true;
return false;
}
/*bool Date::operator<(const Date& d)
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month < d._month)
{
return true;
}
else if (_month == d._month)
{
return _day < d._day;
}
}
return false; //包括_year>d._year和_month>d._month
}*/
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); // 复用 ==
}
3.6 日期 - 日期(计算天数差)
cpp
int Date::operator-(const Date& d)
{
int flag = 1;
Date max = *this;
Date min = d;
// 如果当前日期更小,说明结果是负数
if (*this < d)
{
min = *this;
max = d;
flag = -1;
}
int n = 0;
while (min != max)
{
++min; // 复用前置++
n++;
}
return n * flag;
}
🐶 🐾 ✨ 🐾 🐶
四、流插入(cout)与流提取(cin)重载
4.1 问题:为什么不能写在类里面?
cpp
class Date
{
public:
// ❌ 错误!第一个参数会变成 this 指针
void operator<<(ostream& out, Date& d);
};
成员函数的第一个参数是隐式的 this,但 cout << d1 需要 ostream 作为第一个参数。
- 解决办法: 写在全局,并用友元访问私有成员。
4.2 友元函数
友元函数可以在类外访问类的私有成员。
cpp
class Date
{
public:
// 声明友元函数
friend ostream& operator<<(ostream& out, Date& d);
friend istream& operator>>(istream& in, Date& d);
private:
int _year;
int _month;
int _day;
};
4.3 实现流插入和流提取
🐾 流插入 cout 的重载
cpp
// 流插入 cout << d1
ostream& operator<<(ostream& out, Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out; // 返回out支持连续输出,out其实就是cout的别名
}
🐾 流提取 cin 的重载
// 流提取 cin >> d1
istream& operator>>(istream& in, Date& d)
{
cout << "请依次输入年月日:";
in >> d._year >> d._month >> d._day;
while (!d.CheckDate()) // 检查合法性
{
cout << "输入的日期非法,请重新输入!" << endl;
in >> d._year >> d._month >> d._day;
}
return in; // 返回in支持连续输入
}
🐾 使用示例:
cpp
Date d1, d2;
cin >> d1 >> d2;
cout << d1 << d2 << endl;
🐶 🐾 ✨ 🐾 🐶
五、知识点汇总
| 知识点 | 核心要点 |
|---|---|
| 构造函数 | 与类同名、无返回值、对象创建时自动调用 |
| 缺省参数位置 | 只能写在声明中,不能写在定义中 |
| 运算符重载 | 返回值 operator符号(参数) |
| 前置++ vs 后置++ | 前置返回引用,后置返回副本(int占位符区分) |
| 复用原则 | 用 += 实现 +,用 == 实现 != |
| 友元函数 | 声明加 friend,可访问私有成员 |
| 流插入/提取 | 必须写在全局,返回引用支持链式操作 |
🐶 🐾 ✨ 🐾 🐶
六、本文的所有代码
🐾 Date.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
//声明与定义分离时,缺省参数只能写在声明中,不能出现在声明和定义
//构造函数初始化对象
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout << "日期非法:";
Print();
}
}
//检查日期是否合法
bool Date::CheckDate()
{
if (_month > 0 && _month < 13 && _day>0 && _day<= GetMonthDay(_year, _month))
{
return true;
}
else
return false;
}
void Date::Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
//日期+=天数
Date& Date::operator+=(int day)
{
_day = day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
}
return *this; //首先引用返回目的是避免传参而减少拷贝构造的次数提高效率
//其次this指针是类的地址,我们要返回类,所以对this的引用
}
//复用+的逻辑来实现+=
Date& Date::operator+=(int day)
{
*this = *this + day;
return *this;
}
//日期+天数
Date Date :: operator+(int day)
{
Date tmp = *this; //通过创建新的局部对象来存放日期,避免对原日期进行修改
/*tmp._day += day;
while (tmp._day > GeyMonthDay(tmp._year, tmp._month))
{
tmp._day-- = GetMonthDay(tmp._day, tmp._month);
tmp._month++;
if (tmp._month > 12)
{
tmp._year++;
tmp._month = 1;
}
}*/
tmp += day; //直接复用上面的+=运算符重载的逻辑,则不需要重复写
return tmp;
//由于tmp是在函数内部创建的局部对象,出了函数责则被自动销毁,所以只能传值返回而不能用引用返回
}
//日期-=天数
Date& Date:: operator-=(int day)
{
_day = day;
while (_day <= 0)
{
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
//复用-的逻辑来实现-=
Date& Date::operator -= (int day)
{
*this = *this - day;
return *this;
}
//日期-天数
Date Date::operator-(int day)
{
Date tmp = *this;
/*tmp._day -= aday;
while (tmp._day = 0)
{
tmp._month--;
if (tmp._month == 0)
{
tmp._year--;
tmp._month = 12;
}
tmp_day += GetMonthDay(tnp._year, tmp._month);
}*/
tmp -= day;
return tmp;//和+运算符重载一样,直接复用上面的-=运算符重载的逻辑,则不需要重复写
}
bool Date::operator<(const Date& d)
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month < d._month)
{
return true;
}
else if (_month == d._month)
{
return _day < d._day;
}
}
return false; //包括_year>d._year和_month>d._month
}
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);//实现了上面的的=两个比较的运算符重载就可以直接进行复用
}
//后置++(d1++)
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}
//前置++(++d1)
Date& Date::operator++()
{
*this += 1;
return *this;
}
//后置--(d1++)
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
//前置--(++d1)
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//日期-日期
int Date::operator-(const Date& d)
{
int flag = 1; //flag为1表示假设大得分日期减去小的日期
Date max = *this;
Date min = d;
if (*this < d) //假设不成立,则说明是小的日期减去大的日期,则得到的结果为负值
{
min = *this;
max = d;
flag = -1;
}
int n = 0;
while (min != max) //通过循环复用++运算符重载来得到两个日期之间的天数
{
++min;
++ n;
}
return n * flag; //如果flag为-1则说明是小的日期减去大的日期,n为正数则需要加负号
}
//流插入cout的重载
ostream& operator<<(ostream& out, Date& d)
{
out << d._day << "年" << d._month << "月" << d._day << "日" << endl;
return out;//返回的 out 其实就是 cout 的别名
}
//流提取 cin 的重载
istream& operator<<(istream& in, Date& d)
{
cout << "请依次输入年月日:";
in >> d._year >> d._month >> d._day;
while (1)
{
if ((!d.CheckDate())) //如果输入的日期非法则需要重新输入
{
cout << "输入的日期非法:";
d.Print();
cout << "请重新输入!" <<endl;
}
else
{
break;
}
}
return in; //返回的 in 其实就是 in 的别名
}
🐾 Date.h
cpp
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
//友元函数的声明:能类外的函数也能直接访问类中的成员
friend ostream& operator<<(ostream& out, Date& d);
friend istream& operator>>(istream& in, Date& d);
//全缺省函数
Date(int year = 2005, int month = 2, int day = 8);
void Print();
//默认是内联inline函数,则不需要为了调用函数而建立栈帧
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
//创建一个月份数组来储存每月对应的天数
static int monthDayArr[13] = { -1,31,28,31,30,31,31,30,31,30,31,30,31 };
//第一个-1是为了补上0这个位置,保证每一个月对应相应的天数
//月份数组元素:-1 31 28 31 30 31 31 30 31 30 31 30 31
//月份数组下标:0 1 2 3 4 5 6 7 8 9 10 11 12
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
return 29; //对应闰年二月的29天
}
return monthDayArr[month];
}
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++(int);//后置++
Date& operator++(); //前置++
Date operator--(int);//后置--
Date& operator--(); //前置--
int operator-(const Date& d);//日期 - 日期
bool CheckDate(); //检查日期是否合法
//void operator <<(ostream& out ,Date& d); //流插入cout的重载
//流插入的重载声明放在类中会导致第一个形参为this指针而不是我们想要的的ostreaam& out
//所以我们只能在全局域中实现流插入的重载
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, Date& d); //流插入 cout 的重载
istream& operator<<(istream& in, Date& d); //流提取 cin 的重载
🐾 Test.cpp
cpp
#pragma once
#include"Date.h"
int main()
{
//+=和+的运算符重载测试
Date d1(2026, 3, 23);
d1.Print();
d1 += 1000;
d1.Print();
Date d2 = d1 + 1000;
d1.Print();
//-=和-的运算符重载测试
Date d1(2026, 3, 23);
d1.Print();
d1 -= 1000;
d1.Print();
Date d2 = d1 - 1000;
d2.Print();
//前置、后置++
Date d1(2026, 3, 23);
d1.Print();
Date d2 = d1;
d2.Print();
Date d3 = ++d1;
Date d4 = d2++;
d3.Print();
d4.Print();
d1.Print();
d2.Print();
//前置、后置--
Date d1(2026, 3, 23);
d1.Print();
Date d2 = d1;
d2.Print();
Date d3 = --d1;
Date d4 = d2--;
d3.Print();
d4.Print();
d1.Print();
d2.Print();
//日期 - 日期
Date d1(2026, 3, 23);
d1.Print();
Date d2(2028, 3, 22);
cout << d2 - d1 << endl;
cout << d1 - d2 << endl;
//流插入 cout 的重载
Date d1;
Date d2;
cout << d1 << d2;
//流提取 cin 的重载
Date d1;
Date d2;
cin >> d1 >> d2;
cout << d1 << d2 << endl;
cout << d1 - d2 << endl;
return 0;
}
🐾 下一篇我们继续学习:
- const 成员函数
- const 对象
- 取地址运算符重载
🐶 🐾 ✨ 🐾 🐶

谢谢你看到这里呀
如果喜欢这篇内容,点个关注,下次更新不迷路✨
👍 点赞 ⭐ 收藏 💬 评论
