免费html网站代码,骨科医院网站模板,怎么样才能自己做网站打广告,如何自己做游戏软件#x1f4f7; 江池俊#xff1a;个人主页 #x1f525; 个人专栏#xff1a;✅C那些事儿 ✅Linux技术宝典 #x1f305; 此去关山万里#xff0c;定不负云起之望 文章目录 引言一、为什么需要运算符重载#xff1f;二、日期类的实现1. 基本框架2. 预备工作3. Date 类… 江池俊个人主页 个人专栏✅C那些事儿 ✅Linux技术宝典 此去关山万里定不负云起之望 文章目录 引言一、为什么需要运算符重载二、日期类的实现1. 基本框架2. 预备工作3. Date 类中六大默认成员函数的使用3.1 全缺省的构造函数3.2 拷贝构造函数3.3 析构函数3.4 赋值运算符重载3.5 const 成员函数3.6 取地址操作符重载和const取地址操作符重载 4. 运算符重载4.1 和 运算符重载4.2 - 和 - 运算符重载4.3 前置 和 后置 运算符重载4.4 前置-- 和 后置-- 运算符重载4.5 和 运算符重载4.6 、 、 和 ! 运算符重载4.7 日期-日期4.8 流插入 和 流提取 运算符重载 5. 日期类源码5.1 Date.h文件5.2 Date.cpp 文件 总结 引言
在C编程中运算符重载是一种强大的功能它允许我们为自定义的数据类型定义运算符的行为。这种特性在创建像日期Date这样的类时尤其有用因为它允许我们更自然、更直观地操作这些类的实例。通过日期类我们还能够实现一个简单的日期时间计数器的功能想必大家都很期待接下来的内容。 在本篇博客中我们将深入探索如何为Date类重载运算符并了解其中的一些最佳实践和潜在陷阱。
注Date.h文件下放日期类的声明代码Date.c文件下放日期类的实现代码 一、为什么需要运算符重载
运算符重载可以让我们使用更直观、更自然的方式来操作日期。
在创建日期类时我们可能希望执行如下操作
对两个日期进行加法运算以得到一个新的日期例如将5天加到今天的日期上。比较两个日期以确定哪个日期更早或更晚。从一个日期中减去另一个日期以得到它们之间的时间差。等等…
为了实现这些操作我们需要为Date类重载相应的运算符如、-、等。 二、日期类的实现
1. 基本框架
class Date
{
public:// 获取某年某月的天数int GetMonthDay(int year, int month) const;// 检查日期是否合法bool CheckInvalid();// 全缺省的构造函数Date(int year 1900, int month 1, int day 1);// 拷贝构造函数// d2(d1)Date(const Date d);// 赋值运算符重载// d2 d3 - d2.operator(d2, d3)Date operator(const Date d);// 析构函数~Date();// 有关日期计算的运算符重载 // 日期天数Date operator(int day);// 日期天数Date operator(int day);// 日期-天数Date operator-(int day);// 日期-天数Date operator-(int day);// 前置Date operator();// 后置Date operator(int);// 后置--Date operator--(int);// 前置--Date operator--();// 比较运算符的重载 // 运算符重载bool operator(const Date d) const;// 运算符重载bool operator(const Date d) const;// 运算符重载bool operator (const Date d) const;// 运算符重载bool operator (const Date d) const;// 运算符重载bool operator (const Date d) const;// !运算符重载bool operator ! (const Date d) const;// 日期-日期 返回天数int operator-(const Date d);// 打印,const成员函数修饰的是成员函数隐含的this指针表明不能修改任何成员变量void Print() const{cout _year - _month - _day endl;}// 友元声明可以在类的任何位置friend ostream operator(ostream out, const Date d);friend istream operator(istream in, Date d);
private:int _year;int _month;int _day;
};// 类外声明
ostream operator(ostream out, const Date d);
istream operator(istream in, Date d);2. 预备工作
在构造日期类时我们很有可能不小心传入错误的日期时间比如 2023-02-29 或 2024-4-31这样的时间所以为了确保日期的正确性在构造函数中我们需要检查日期的合法性因此在写构造函数前需要先实现两个成员函数
CheckInvalid() 函数功能是检查日期是否合法GetMonthDay(int year, int month) 函数功能是获取某年某月的天数
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month) const
{static int days[13] { 0,31,28,31,30,31,30,31,30,31,31,30,31 };if (month 2 ((year % 4 0 year % 100 ! 0) || (year % 400 0))){return 29;}return days[month];
}// 检查日期是否合法
bool Date::CheckInvalid()
{if (_year 0|| _month 1 || _month 12|| _day 1 || _day GetMonthDay(_year, _month)){return false;}return true;
}注意这里 days 数组被声明为静态数组静态数组在程序的生命周期内只会被初始化一次。由于 GetMonthDay 函数被频繁调用将其声明为静态可以确保它只在程序开始时分配一次内存而不是每次调用函数时都重新分配。这可以提高空间效率。
3. Date 类中六大默认成员函数的使用
3.1 全缺省的构造函数
// 全缺省的构造函数
Date(int year 1900, int month 1, int day 1)
{_year year;_month month;_day day;if (!CheckInvalid()){cout *this 构造日期非法 endl;exit(-1);}
}通过构造函数即可对实例化对象进行初始化。
3.2 拷贝构造函数
拷贝构造同类型的一个已经存在的对象去初始化一个新的要创建的对象
// 拷贝构造函数
// d2(d1)
Date(const Date d)
{_year d._year;_month d._month;_day d._day;
}3.3 析构函数
// 析构函数
~Date()
{//cout ~Date() endl;
}日期类并 没有申请资源动态开辟内存打开文件等所以这里的析构函数可写可不写系统默认生成的就够用。
3.4 赋值运算符重载
赋值重载已经存在的两个对象一个拷贝赋值给另一个
// 赋值运算符重载
// d2 d3 - d2.operator(d2, d3)
Date operator(const Date d)
{if (this ! d){_year d._year;_month d._month;_day d._day;}return *this;
}3.5 const 成员函数
// 举例
// 打印,const成员函数修饰的是成员函数隐含的this指针表明不能修改任何成员变量
void Print() const
{cout _year - _month - _day endl;
}将const修饰的“成员函数”称之为const成员函数const修饰类成员函数实际修饰该成员函数隐含的this指针表明在该成员函数中 不能对类的任何成员进行修改。
如果我们只需要访问成员变量而非修改成员变量那么此时我们就可以使用 const成员函数 来提高代码的健壮性。
3.6 取地址操作符重载和const取地址操作符重载
// 取地址操作符重载
Date* operator()
{return this;//return nullptr;
}// const取地址操作符重载
const Date* operator()const
{return this;//return nullptr;
}这两个运算符一般不需要重载使用编译器生成的默认取地址的重载即可只有特殊情况才需要重载比如想让别人获取到指定的内容
这里以注释的两个return语句为例可以返回空指针给别人让对方找不到指定的类对象。所以大多数情况下我们都不需要重载这两个成员函数使用编译器默认生成的即可 4. 运算符重载
此处详细讲解 和 运算符的重载- 和 - 运算符的重载与其类似。
因此对于前置和后置前置- -和后置- -运算符重载详细讲解前者
4.1 和 运算符重载
注实现了 运算符的重载后对于 运算符的重载就可以利用 运算符来实现。
// 日期天数
Date Date::operator(int day)
{_day day;while (_day GetMonthDay(_year, _month)){_day - GetMonthDay(_year, _month);if (_month 12){_month 1;_year;}else{_month;}}return *this;
}
// 日期天数
Date Date::operator(int day)
{Date temp *this; // 调用拷贝构造因为temp不是一个已存在的对象temp day;return temp;
}operator 的算法思想和步骤 输入Date对象的一个引用和一个整数代表天数。算法思想将给定的天数加到当前日期的天数上并相应地调整月份和年份以确保日期仍然有效。步骤 将当前日期的天数_day与给定的天数相加。检查更新后的天数是否超过了当前月份的天数通过调用GetMonthDay函数。如果超过了从当前月份的天数中减去超出的部分并更新月份和年份。 如果月份是12月将月份设置为1月年份加1。否则将月份加1。 返回Date对象的引用以便进行链式操作。 operator 的算法思想和步骤 输入Date对象的一个实例和一个整数代表天数。算法思想创建一个Date对象的副本并将给定的天数加到副本上然后返回调整后的日期副本。步骤 创建一个Date类型的临时对象temp并通过拷贝构造函数将其初始化为当前Date对象的副本。使用operator函数将给定的天数加到temp对象上。返回调整后的temp对象值拷贝返回。
注返回Date对象的引用在operator中允许进行链式操作如 dateObj 5 3。
4.2 - 和 - 运算符重载
// 日期-天数
Date Date::operator-(int day)
{Date temp *this;temp - day;return temp;
}
// 日期-天数
Date Date::operator-(int day)
{while (day _day){day - _day;if (_month 1){_month 12;_year--;_day GetMonthDay(_year, _month);}else{_month--;_day GetMonthDay(_year, _month);}}_day - day;return *this;
}4.3 前置 和 后置 运算符重载 前置返回1之后的结果 注意this指向的对象函数结束后不会销毁故以引用方式返回提高效率 后置前置和后置都是一元运算符为了让前置与后置形成能正确的重载C规定后置重载时多增加一个 int 类型的参数但调用函数时该参数不用传递编译器自动传递 注意后置是先使用后1因此需要返回1之前的旧值故需在实现时需要先将 this 保存一份然后给 this 1而temp是临时对象因此只能以值的方式返回不能返回引用。
// 前置
Date Date::operator()
{*this 1;return *this;
}
// 后置
Date Date::operator(int)
{Date temp *this;*this 1;return temp;
}4.4 前置-- 和 后置-- 运算符重载
// 后置--
Date Date::operator--(int)
{Date temp *this;*this - 1;return temp;
}
// 前置--
Date Date::operator--()
{*this - 1;return *this;
}4.5 和 运算符重载
注意对于比较运算的重载只要先重载了 和 运算符其他运算符的重载就可以利用这两者来实现。
// 运算符重载
bool Date::operator(const Date d) const
{if (_year d._year)return true;else if (_year d._year){if (_month d._month)return true;else if (_month d._month){if (_day d._day)return true;}}return false;
}// 运算符重载
bool Date::operator(const Date d) const
{return _year d._year _month d._month _day d._day;
}4.6 、 、 和 ! 运算符重载
// 运算符重载
bool Date::operator (const Date d) const
{return *this d || *this d;
}
// 运算符重载
bool Date::operator (const Date d) const
{return !(*this d);
}
// 运算符重载
bool Date::operator (const Date d) const
{return !(*this d);
}
// !运算符重载
bool Date::operator ! (const Date d) const
{return !(*this d);
}4.7 日期-日期
// 日期-日期 返回天数
int Date::operator-(const Date d)
{int flag 1;Date maxDate *this;Date minDate d;if (*this d){flag -1;maxDate d;minDate *this;}int n 0;while (maxDate ! minDate){minDate;n;}return flag * n;
}思路 比较日期首先比较当前对象*this和传入的日期对象d确定哪个日期更大哪个更小。确定符号根据日期的大小关系确定结果的符号。如果当前对象更大则结果为正如果传入的日期更大则结果为负。计算天数差从较小的日期开始逐步增加一天直到两个日期相等。每次增加一天就增加计数器的值。返回结果根据符号和天数差计算最终的结果并返回。 步骤 声明变量 flag用于表示结果的符号初始化为1表示正数。maxDate和minDate用于存储两个日期中较大和较小的日期初始时分别设置为当前对象*this和传入的日期d。 比较日期并确定符号 使用if语句比较*this和d。如果*this小于d则交换maxDate和minDate的值并将flag设置为-1。 初始化计数器 声明n并初始化为0用于计算天数差。 计算天数差 使用while循环每次循环将minDate增加一天即调用minDate并增加计数器n的值。循环继续直到maxDate和minDate相等。 返回结果 将flag与n相乘得到最终的结果并返回。
4.8 流插入 和 流提取 运算符重载
有了日期类那么我们需要打印日期信息该怎么办呢
我们可以实现一个 Print 成员函数来实现
// 打印,const成员函数修饰的是成员函数隐含的this指针表明不能修改任何成员变量
void Print() const
{cout _year - _month - _day;
}但是每次都要调用函数来打印日期是不是太麻烦了有没有更简单的方法就像内置类型int、char等那样直接使用 cout 打印呢
在C中对于自定义类型可以重载流插入 来实现像内置类型一样的输出操作。 cout 是一个 ostream 类型的对象。
// 流插入 重载
ostream operator(ostream out, const Date d)
{// 输出格式化字符串到输出流 out d._year - d._month - d._day endl;// 返回输出流引用以支持链式操作return out;
}【思路】
定义重载函数定义一个函数接受一个ostream引用和一个const Date引用作为参数。输出格式化字符串在函数内部使用输出流out来格式化并输出Date对象的年、月、日信息。返回输出流引用为了支持链式输出函数应返回ostream的引用。
注意事项
在使用流插入 时要确保第一个参数是ostream的对象如果是在重载的时候写成成员函数编译器就会将第一个参数默认传入this指针因此只能将其重载成全局函数传入两个参数第一个为ostream的对象第二个为Date类对象。相应的重载成了全局函数必然会导致类中的私有成员不能被访问那么这又该如何呢这里有两种解决办法 创建公有的成员函数getYeargetMonthgetDay这样就可以通过这些函数去访问私有成员变量java很喜欢使用这种方式。使用友元函数当一个函数或类被声明为另一个类的友元时它可以访问该类的所有成员包括私有成员。
既然有流插入 运算符的重载那么必然就有流提取 运算符的重载。
在C中对于自定义类型可以重载流提取 来实现像内置类型一样的输入操作。 cin 是一个 istream 类型的对象。
// 流提取 重载
istream operator(istream in, Date d)
{while (1){cout 请依次输入年 月 日;in d._year d._month d._day;if (!d.CheckInvalid()){cout 亲你输入了无效日期请重新输入 endl;}else{break;}}// 返回输入流引用以支持链式操作return in;
}【思路】
定义重载函数定义一个函数接受一个istream引用和一个Date引用作为参数。输入日期从输入流中读取年、月、日并尝试赋值给Date对象。验证日期调用Date对象的CheckInvalid方法来检查输入的日期是否有效。错误处理如果日期无效则输出错误消息并继续循环要求用户重新输入。返回输入流引用如果日期有效则退出循环并返回输入流的引用以支持链式输入。 5. 日期类源码
5.1 Date.h文件
#pragma once#include iostreamusing namespace std;class Date
{
public:// 获取某年某月的天数int GetMonthDay(int year, int month) const;// 检查日期是否合法bool CheckInvalid();// 全缺省的构造函数Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;if (!CheckInvalid()){cout *this 构造日期非法 endl;exit(-1);}}// 拷贝构造函数// d2(d1)Date(const Date d){_year d._year;_month d._month;_day d._day;}// 赋值运算符重载// d2 d3 - d2.operator(d2, d3)Date operator(const Date d){if (this ! d){_year d._year;_month d._month;_day d._day;}return *this;}// 析构函数~Date(){//cout ~Date() endl;}// 取地址操作符重载Date* operator(){return this;//return nullptr;}// const取地址操作符重载const Date* operator()const{return this;//return nullptr;}// 日期天数Date operator(int day);// 日期天数Date operator(int day);// 日期-天数Date operator-(int day);// 日期-天数Date operator-(int day);// 前置Date operator();// 后置Date operator(int);// 后置--Date operator--(int);// 前置--Date operator--();// 运算符重载bool operator(const Date d) const;// 运算符重载bool operator(const Date d) const;// 运算符重载bool operator (const Date d) const;// 运算符重载bool operator (const Date d) const;// 运算符重载bool operator (const Date d) const;// !运算符重载bool operator ! (const Date d) const;// 日期-日期 返回天数int operator-(const Date d);// 打印,const成员函数修饰的是成员函数隐含的this指针表明不能修改任何成员变量void Print() const{cout _year - _month - _day endl;}// 友元声明可以在类的任何位置friend ostream operator(ostream out, const Date d);friend istream operator(istream in, Date d);
private:int _year;int _month;int _day;
};// 类外声明
ostream operator(ostream out, const Date d);
istream operator(istream in, Date d);5.2 Date.cpp 文件
#define _CRT_SECURE_NO_WARNINGS 1#include Date.h// 获取某年某月的天数
int Date::GetMonthDay(int year, int month) const
{static int days[13] { 0,31,28,31,30,31,30,31,30,31,31,30,31 };if (month 2 ((year % 4 0 year % 100 ! 0) || (year % 400 0))){return 29;}return days[month];
}// 日期天数
Date Date::operator(int day)
{_day day;while (_day GetMonthDay(_year, _month)){_day - GetMonthDay(_year, _month);if (_month 12){_month 1;_year;}else{_month;}}return *this;
}
// 日期天数
Date Date::operator(int day)
{Date temp *this; // 调用拷贝构造因为temp不是一个已存在的对象temp day;return temp;
}
// 日期-天数
Date Date::operator-(int day)
{Date temp *this;temp - day;return temp;
}
// 日期-天数
Date Date::operator-(int day)
{while (day _day){day - _day;if (_month 1){_month 12;_year--;_day GetMonthDay(_year, _month);}else{_month--;_day GetMonthDay(_year, _month);}}_day - day;return *this;
}// 前置
Date Date::operator()
{*this 1;return *this;
}
// 后置
Date Date::operator(int)
{Date temp *this;*this 1;return temp;
}
// 后置--
Date Date::operator--(int)
{Date temp *this;*this - 1;return temp;
}
// 前置--
Date Date::operator--()
{*this - 1;return *this;
}// 运算符重载
bool Date::operator(const Date d) const
{if (_year d._year)return true;else if (_year d._year){if (_month d._month)return true;else if (_month d._month){if (_day d._day)return true;}}return false;
}
// 运算符重载
bool Date::operator(const Date d) const
{return _year d._year _month d._month _day d._day;
}
// 运算符重载
bool Date::operator (const Date d) const
{return *this d || *this d;
}
// 运算符重载
bool Date::operator (const Date d) const
{return !(*this d);
}
// 运算符重载
bool Date::operator (const Date d) const
{return !(*this d);
}
// !运算符重载
bool Date::operator ! (const Date d) const
{return !(*this d);
}// 日期-日期 返回天数
int Date::operator-(const Date d)
{int flag 1;Date maxDate *this;Date minDate d;if (*this d){flag -1;maxDate d;minDate *this;}int n 0;while (maxDate ! minDate){minDate;n;}return flag * n;
}// 检查日期是否合法
bool Date::CheckInvalid()
{if (_year 0|| _month 1 || _month 12|| _day 1 || _day GetMonthDay(_year, _month)){return false;}return true;
}// 流插入 重载
ostream operator(ostream out, const Date d)
{// 输出格式化字符串到输出流 out d._year - d._month - d._day endl;// 返回输出流引用以支持链式操作return out;
}
// 流提取 重载
istream operator(istream in, Date d)
{while (1){cout 请依次输入年 月 日;in d._year d._month d._day;if (!d.CheckInvalid()){cout 亲你输入了无效日期请重新输入 endl;}else{break;}}// 返回输入流引用以支持链式操作return in;
}总结 此日期类是一个非常适合大家训练自己对运算符重载知识理解和掌握的小项目它是C引用、传值/传引用返回、拷贝构造、赋值重载、运算符重载、const成员函数、const修饰参数等知识的一个融合相信大家在上手练习的过程中能收获颇丰。