【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载)

这篇具有很好参考价值的文章主要介绍了【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

【C++初阶】四、类和对象
(构造函数、析构函数、拷贝构造函数、赋值运算符重载函数)-CSDN博客

 =========================================================================

                     

一 . 日期类的完善

此次日期类成员函数采用声明和定义实现分离的方式实现

成员函数声明和定义(实现)分离的好处:

成员函数的声明写在头文件方便我们查看这个类中有哪些成员函数

具体实现再到成员函数实现文件.cpp文件中查看

                        

(详细解释在图片的注释中,代码分文件放下一标题处)

                           

Date构造函数 -- 全缺省构造函数优化

                 

  • 实例化对象所给的初始化参数可能不合理比如给了负数的日期),
    需要进行特殊处理报错

                   
  • 注意
    全缺省构造函数的缺省参数只能在声明或实现中给
    不能两边都给缺省参数不然编译器不知道要使用哪边的缺省参数
    而且两边的缺省参数给得还可能不一样
    所以这里选择在声明中给缺省参数实现时并没有给
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

测试:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                          

                          
---------------------------------------------------------------------------------------------

                       

Print函数 -- 打印日期函数

图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                          

                          
---------------------------------------------------------------------------------------------

                       

GetMonthday“辅助”函数 -- 获取当前月份日期函数

                 

  • 因为考虑到各月份日期可能不一样二月还需要考虑闰年还是平年
    所以可以单独写一个函数处理月份的情况方便后续成员函数的实现
                       
  • assert断言防止year年份month月份传错
                       
  • 一年有12个月定义一个有13个元素的数组
    不使用第一个元素之后的12个元素分别为1~12月各个月的对应日期平年
                     
  • 单独判断2月的情况如果year是闰年修改2月的日期29天
                    
  • 最后通过数组下标返回对应月的日期
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                          

                          
---------------------------------------------------------------------------------------------

                       

operator==函数  --  “==”运算符重载函数

                 

  • 通过隐藏的this指针依次判断两日期日是否相同并返回结果
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator!=函数  --  “!=”运算符重载函数

                 

  • 复用==运算符重载函数对其结果取反就能实现!=运算符重载函数
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator>函数  --  “>”运算符重载函数

                 

  • 先比较年份,“年大就大”;
    如果年份相等,“月大就大”;
    如果年份相等月份相等,“天大就大”;
    返回true
               
  • 除此以外就是两日期小于等于的情况了返回false
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator>=函数  --  “>=”运算符重载函数

                 

  • 复用>==运算符重载函数即可实现">="运算符重载函数
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator<函数  --  “<”运算符重载函数

                 

  • 复用>=运算符重载函数即可实现"<"运算符重载函数
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator<=函数  --  “<=”运算符重载函数

                 

  • 复用>运算符重载函数即可实现"<="运算符重载函数
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator+=函数  --  “+=”运算符重载函数(日期+=整型)

                 

  • 我们执行+=操作有时右值可能是一个负数实际执行时就是减法
    所以需要进行特殊处理
             
  • 如果右值正数则将右值天数加到日期左值
    再按需进行进位操作
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator+函数  --  “+”运算符重载函数(日期+整型)

                 

  • +=不同,“+不会改变左值
    所以为了保证左值不变可以对左值进行拷贝拷贝出一个tmp对象
    tmp复用+=并返回即可实现+
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

测试:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

先实现“+=”再复用实现“+” 还是 先实现“+”再复用实现“+=”

                 

  • 上面我们是先实现了+=”,再复用+=实现了+”,
    先实现的+=不需要进行对象的拷贝
    而复用+=实现的+中需要进行两次对象的拷贝
    一次是拷贝生成tmp对象还有一次是传值返回需要拷贝一次
    所以总共需要拷贝两次对象

                        
  • 而如果是先实现+”,再复用+实现+=的话
    先实现的+中需要进行两次对象的拷贝
    一次是拷贝生成tmp对象还有一次是传值返回需要拷贝一次
    而复用+实现的+=中需要进行三次对象的拷贝
    复用的+需要进行两次拷贝,“+完后还要进行=拷贝一次
    所以总共需要拷贝三次对象
                        

所以最好还是先实现+=”,再复用+=实现+比较好
减少对象的拷贝次数提高效率|
              
 

                          
---------------------------------------------------------------------------------------------

                       

operator-=函数  --  “-=”运算符重载函数(日期-=整型)

                 

  • 我们执行“-=操作有时右值可能是一个负数实际执行时就是加法
    所以需要进行特殊处理
             
  • 如果右值正数则将右值天数减到日期左值
    再按需进行借位操作
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator-函数  --  “-”运算符重载函数(日期-整型)

                 

  • -=不同,“-不会改变左值
    所以为了保证左值不变可以对左值进行拷贝拷贝出一个tmp对象
    tmp复用-=并返回即可实现-
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

(“-100天” / “-10000天”)

                        

                          
---------------------------------------------------------------------------------------------

                       

operator-函数  --  “-”运算符重载函数(日期-日期

                 

  • 先假设左值日期较大日期max右值日期较小日期min
    此时两日期相减的天数会是定义flag变量1正数
    参数给得没问题左大右小减出来应该是一个正数
                  
  • 如果我们假设错了则将较大的右值日期赋给max较小的左值日期赋给min
    按照-减号的逻辑在假设的这种情况下最终返回的天数会是
    flag变量变为-1负数
    参数可能给反了左小右大减出来应该是一个负数
                   
  • 我们不使用传统的日期-日期方法因为有太多情况要处理
    通过前两个操作max是两日期中的较大日期min是两日期中的较小日期
    使用while循环循环一次++min直到min==max为止
    此时循环次数就是两日期相差的天数
    (关于日期类的“++运算符重载函数后面会实现)
                         
  • 最终返回 循环日期*flag 即可
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

测试:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator++函数  --  “++”运算符重载函数(日期前置++)

                 

  • 前置++” --  先+1再使用逻辑上
    直接复用+=实现++”,再返回++后的日期实现上
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

测试:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator++函数  --  “++”运算符重载函数(日期后置++)

                 

  • 因为++运算符前置++后置++”,所以需要分别重载实现重载两次
    但是要让前置++后置++构成重载函数
    需要函数名相同参数不同但不管是哪种++”,
    都只有一个操作数隐藏的this指针就够用了
    所以为了区分前置++后置++”,“后置++参数会多一个int必须是int类型),
    也可以相应的给个实参int i
    但是实现过程不会使用到i所以一般直接写int即可
                            
  • 后置++” --  先使用再+1逻辑上
    在+1前先拷贝当前日期tmp再对日期+1最后返回的是+1前日期tmp实现上
                      
  • 前置++后置++对比
    前置++效率比较高
    因为后置++为了实现后置需要进行两次拷贝
    所以自定义类型中使用++的话最好还是使用前置++比较好效率高
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

测试:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator--函数  --  “--”运算符重载函数(日期前置--)

                 

  • 前置--” --  先-1再使用逻辑上
    直接复用-=实现--”,再返回--后的日期实现上
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                        

                          
---------------------------------------------------------------------------------------------

                       

operator--函数  --  “--”运算符重载函数(日期后置--)

                 

  • 后置--” --  先使用再-1逻辑上
    在-1前先拷贝当前日期tmp再对日期-1最后返回的是-1前日期tmp实现上
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

日期类完善后代码

Date.h -- 日期类头文件:

#pragma once

//包含头文件:
#include <iostream>
#include <assert.h>

//完全展开std命名空间:
using namespace std;

//日期类:
class Date
{
public:
	//全缺省构造函数:
	Date(int year = 1, int month = 1, int day = 1);


	//打印日期函数:
	void Print();
	//“辅助”完成“+、+=”运算符重载的函数:
	/*
	* 因为考虑到各月份日期可能不一样,
	* 二月还需考虑闰年还是平年,
	* 所以可以单独写一个函数处理月份的情况:
	*/
	int GetMonthDay(int year, int month);


	//“==”运算符重载函数:
	//判断对象是否相等的函数(以日期类为例):
	bool operator==(const Date& y);  //等于
	//“!=”运算符重载函数:
	bool operator!=(const Date& y);  //不等于
	//“>”运算符重载函数:
	//写一个函数判断自定义类型大小(以日期类对象为例):
	bool operator>(const Date& y);  //大于	
	//“>=”运算符重载函数:
	bool operator>=(const Date& y);  //大于等于
	//“<”运算符重载函数:
	bool operator<(const Date& y);  //小于
	//“<=”运算符重载函数:
	bool operator<=(const Date& y);  //小于等于


	//“+=”运算符重载函数:
	Date& operator+=(int day);  // “+=” 才会改变d1对象
	//“+”运算符重载函数:
	Date operator+(int day);  // “+” 不会改变d1对象
	//“-=”运算符重载函数:
	Date& operator-=(int day);  // “-=” 会改变d1对象
	//“-”运算符重载函数 -- “日期 - 整型”:
	Date operator-(int day);  // “-” 不会改变d1对象
	//“-”运算符重载函数 -- “日期-日期”:
	int operator-(const Date& d);


	//“++”运算符重载函数 -- “前置++”:
	Date& operator++();
	//“++”运算符重载函数 -- “后置++”:
	Date operator++(int);
	//“--”运算符重载函数 -- “前置--”:
	Date& operator--();
	//“--”运算符重载函数 -- “后置--”:
	Date operator--(int);
	

//private:
	int _year;
	int _month;
	int _day;
};

                     

                     


                    

Date.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 ( _year < 1 ||
	    _month < 1 || _month > 12 ||
		  _day < 1 || _day > GetMonthDay(_year, _month))
	/*
	* 日期不合理的情况:
	* 年份/月份/日期 < 1 或者 
	* 月份 > 12 或者 日期大于当前月份天数
	*(当前月份通过GetMonthDat函数获得)
	*/
	{
		//通过断言报错:
		assert(false);
	}
}
/*
* 注:这里的缺省参数只能在声明或实现中给,
* 如果声明和实现中都给了缺省参数的话,
* 编译器不知道要用哪边的缺省参数,
* 两边的缺省参数还可能不一样,所以这里是
* 在声明中给了缺省参数,实现时并没有给
*/



//打印日期函数:
void Date::Print()
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

//“辅助”完成“+、+=”运算符重载的函数:
/*
* 因为考虑到各月份日期可能不一样,
* 二月还需考虑闰年还是平年,
* 所以可以单独写一个函数处理月份的情况:
*/
int Date::GetMonthDay(int year, int month)
{
	//assert断言防止year或month传错:
	assert(year >= 1 && month >= 1 && month <= 12);

	int monthArray[13] = { 0, 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; //返回闰年二月的29天
	}

	//把数组下标当作月份,返回对应月份:
	return monthArray[month];
}



//“==”运算符重载函数:
//判断对象是否相等的函数(以日期类为例):
bool Date::operator==(const Date& y)  //等于
/*
* (注:)在类中的话,隐藏的this指针是第一个参数,
* == 有两个操作数,this指针 和 y 就是两个操作数,
*(运算符重载,这个运算符操作数和要和函数的参数匹配)
*/
{
	//比较两日期类对象是否相等只要判断两者年月日是否相等即可:
	return _year == y._year
		&& _month == y._month
		&& _day == y._day;

	/*
	* 成员变量虽然是私有的,
	* 但在类中通过this指针访问私有成员变量是可以的
	*/
}

//“!=”运算符重载函数:
bool Date::operator!=(const Date& y) //不等于
{
	//直接返回判断结果:
	return !(*this == y);
	/*
	*  *this即左值对象,y即右值对象
	* 再通过operator==重载函数判断是否相等
	*/
}

//“>”运算符重载函数:
//写一个函数判断自定义类型大小(以日期类对象为例):
bool Date::operator>(const Date& y)  //大于
/*
* > 同样有两个操作数,隐藏的this指针是第一个参数
*/
{
	//“年大就大”:
	if (_year > y._year)
	{
		return true;
	}
	//"年相等,月大就大":
	else if (_year == y._year && _month > y._month)
	{
		return true;
	}
	//"年相等,月相等,天大就大":
	else if (_year == y._year && _month == y._month && _day > y._day)
	{
		return true;
	}

	//上面已经写出了所有大于的情况,执行到这说明是小于等于:
	return false;  //返回false
}

//“>=”运算符重载函数:
bool Date::operator>=(const Date& y)  //大于等于
{
	//直接复用“>”和“==”运算符重载函数即可:
	return *this > y || *this == y;
	/*
	* *this即左值对象,y即右值对象
	* 通过复用“>”和“==”运算符重载函数判断并返回结果即可,
	*		“>=” --> “>” 或 “==”
	*/
}

//“<”运算符重载函数:
bool Date::operator<(const Date& y)  //小于
{
	//直接复用“>=”运算符重载函数即可:
	return !(*this >= y);
	/*
	* *this即左值对象,y即右值对象
	* 通过复用“>=”运算符重载函数即可,
	* 如果不是“>=”,那就是“<”了
	*/
}

//“<=”运算符重载函数:
bool Date::operator<=(const Date& y) //小于等于
{
	//直接复用“>”运算符重载函数即可:
	return !(*this > y);
	/*
	* *this即左值对象,y即右值对象
	* 通过复用“>”运算符重载函数即可,
	* 如果不是“>”,那就是“<=”了
	*/
}
/*
* 对于这类用于判断的运算符,
* 只要实现了“>”(或“<”)和 “=”,
* 其它的判断运算符就只要通过复用这两个
* 运算符即可实现了(逻辑取反)
*/



//“+=”运算符重载函数:
Date& Date::operator+=(int day) // += 才会改变d1对象
//为了防止拷贝返回,使用引用返回
{
	/*
	* 可能有坏蛋右值传了个负数过来,
	* 如:d1 += -100;
	* 这时就要进行特殊处理了:
	*/
	if (day < 0)
		//如果右值是一个负数:
	{
		//使用“-=”运算符重载函数处理该情况:
		return *this -= (-day);
		/* 
		* *this = *this - (-day)
		* (-day)负负得正,让日期减去day(变正),
		* 返回日期减去day后的日期
		*/
	}

	//思路:当月天数满了进位到月,月满了则进位到年:

	_day += day; //先将要加的天数加到当前的天数上
	// this.day = this.day + (day)

	//开始进位(如果需要的话):
	while (_day > GetMonthDay(_year, _month))
		/*
		* 先通过GetMonthDay函数获得当月的天数,
		* 再比较相加后的天数是否超过了当月的天数,
		* 只要超过了则进行进位,进位到月:
		* (while循环到没超过为止)
		*/
	{
		//天数减去一轮当前月天数:
		_day -= GetMonthDay(_year, _month);

		//减去的一轮当前月天数进位到月中:
		++_month;

		//如果当前月进位后超过了12个月:
		if (_month == 13)
		{
			//将一轮月份进位到年:
			++_year;

			//将月份重置为1月:
			_month = 1;
		}
	}

	return *this;
}

//“+”运算符重载函数:
Date Date::operator+(int day)
{
	Date tmp(*this);
	/*
	* 为了实现加法,加了后不改变d1对象(+=才会改变)
	* 先通过*this(即d1)拷贝出一个tmp,
	* 对tmp进行加法操作就不会改变d1对象了
	*/

	/*
	* 复用 “+=运算符重载” ,只要 += 到d1的拷贝tmp上即可,
	* 就不会改变到d1对象,通过tmp返回d1的加法结果:
	*/
	tmp += day;

	//通过tmp返回d1的加法结果:
	return tmp;
	/*
	* 这里tmp是d1的拷贝,出了函数就销毁了,
	* 所以需要传值返回拷贝一份回主函数
	*/
}

“+”运算符重载函数:
d1 + 100(整数) ,计算d1日期的100天后的日期:
//Date Date::operator+(int day) // “+” 不会改变d1对象
//	//为了防止拷贝返回,使用引用返回
//{
//	Date tmp(*this); 
//	/*
//	* 为了实现加法,加了后不改变d1对象(+=才会改变)
//	* 先通过*this(即d1)拷贝出一个tmp,
//	* 对tmp进行加法操作就不会改变d1对象了
//	*/
//
//	//思路:当月天数满了进位到月,月满了则进位到年:
//
//	tmp._day += day; //先将要加的天数加到当前的天数上
//
//	//开始进位(如果需要的话):
//	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//		/*
//		* 先通过GetMonthDay函数获得当月的天数,
//		* 再比较相加后的天数是否超过了当月的天数,
//		* 只要超过了则进行进位,进位到月:
//		* (while循环到没超过为止)
//		*/
//	{
//		//天数减去一轮当前月天数:
//		tmp._day -= GetMonthDay(tmp._year, tmp._month);
//
//		//减去的一轮当前月天数进位到月中:
//		++tmp._month;
//
//		//如果当前月进位后超过了12个月:
//		if (tmp._month == 13)
//		{
//			//将一轮月份进位到年:
//			++tmp._year;
//
//			//将月份重置为1月:
//			tmp._month = 1;
//		}
//	}
//
//return tmp;
///*
//* 这里tmp是d1的拷贝,出了函数就销毁了,
//* 所以需要传值返回拷贝一份回主函数
//*/
//}

“+=”运算符重载函数:
//Date& Date::operator+=(int day)
//{
//	//复用“+”运算符重载函数:
//	*this = *this + day;
//	/*
//	* 这里的*this(左值对象)和day(右值对象),
//	* 都是已经存在的对象,把已经存在的对象赋值给
//	* 另一个已经存在的对象上,需要使用到赋值运算符重载函数
//	* 
//	* 使用“+”运算符重载函数将day日期加到*this日期上,
//	* 再使用赋值运算符重载函数把加上的日期赋值给*this,
//	* 完成“+”运算符重载函数的复用
//	*/
//	
//	//返回“+=”结果:
//	return *this;
//}
/*
*				一:
* 如果先实现了“+”运算符重载函数,
* 可以复用“+”运算符重载函数实现“+=”运算符重载函数
* 
*				二:
* 如果先实现了“+=”运算符重载函数,
* 那就可以复用“+=”运算符重载函数实现“+”运算符重载函数
* 
* 第一种方法的“+”需要进行两次对象拷贝,
* “+=”通过复用“+”实现,需要三次对象拷贝,
* 所以该方法总共需要五次拷贝
* 
* 第二种方法的“+=”不需要进行对象拷贝,
* “+”中需要两次对象拷贝,该方法总共需要两次拷贝
* 
* 所以还是通过第二种方法实现“+=”和“+”比较好,
* 而且实现加法最好使用“+=”,而不是“+”,
* 因为“+=”不需要进行拷贝,“+”需要拷贝两次对象
*/

//“-=”运算符重载函数:
Date& Date::operator-=(int day)
{
	/*
	* 可能有坏蛋右值传了个负数过来,
	* 如:d1 -= -100;
	* 这时就要进行特殊处理了:
	*/
	if (day < 0)
		//如果右值是一个负数:
	{
		//使用“+=”运算符重载函数处理该情况:
		return *this += (-day);
		/*
		* *this = *this + (-day)
		* (-day)负负得正,让日期加上day(变正),
		* 返回日期加上day后的日期
		*/
	}

	//先将要减的日期day减到对象的_day变量上:
	_day -= day; //注:这里是内置类型(int)的“-=”

	/*
	* 减去day后日期不能小于等于0,
	*(日期不能为负数,也不能有0日)
	* 需要跟上个月份借天数:
	*/
	while (_day <= 0)
	{
		--_month; //月份-1,跟上个月借天数
		
		//如果月份被"减没了"(月份为0):
		if (_month == 0)
		{
			//再跟上一年借12各月份:
			--_year; //年份-1

			//月份重置为12个月:
			_month = 12; 
		}

		//将跟当月借的天数加到_day中:
		_day += GetMonthDay(_year, _month);

		//循环到_day天数>0为止
	}

	//返回左值对象减去一个天数后的日期:
	return *this; //即d1(左值对象)
}

//“-”运算符重载函数 -- “日期-整型”:
Date Date::operator-(int day)
{
	//“-”不会改变对象,所以先拷贝一个左值对象出来:
	Date tmp(*this); 
	/*
	* *this即左值对象,
	* 通过拷贝构造函数对其拷贝出一个tmp对象出来
	*/

	//复用上面的“-=”运算符重载函数实现“-”:
	tmp -= day;
	/*
	* “-=”改变的是*this的拷贝tmp,
	* 所以不会影响到*this
	*/

	//返回日期对象减去day天后的日期:
	return tmp;
}

//“-”运算符重载函数 -- “日期-日期”:
int Date::operator-(const Date & d)
{
	//假设左值日期为大值:
	Date max = *this;  //存放左值对象
	//假设右值日期为小值:
	Date min = d;  //存放右值对象
	
	//“如果max要减去min”,相差天数可能为正可能为负:
	int flag = 1; //flag为1即为正,flag为-1即为负

	//如果我们假设的“左大右小”错了:
	if (*this < d)
	{
		//那就让max存储d(大值),
		//min存储*this(小值):
		max = d;  //存储大值d
		min = *this;  //存储小值*this

		//那么此时max减去min的话,相差天数应是负的:
		flag = -1;
	}

	//定义一个变量n:
	int n = 0;  //存储max和min相差的天数
	while (min != max)
		//当min还小于max时:
	{
		//min++,min加一天:
		++min;
		//n存储的相差天数+1:
		++n;

		/*
		* 当min加到和max相等时,
		* n就是max和min相差的天数,
		* 巧用“++”运算符重载函数就能计算出相差天数,
		* 而不是“年-年,月-月,天-天”的方法,
		* 不需要分析大量的情况,逻辑简单,
		* 日期相差比较大的时候,可能执行会比较“笨”,
		* 但这对计算机来说不算什么
		*/
	}

	return n * flag;
	/*
	*			  max - min:
	* 假设我们一开始假设是对的:max > min
	* 那么 n*flag = 相差天数(正)*1 ,返回正日期
	* 如果我们假设错了:max < min
	* 那么 n*flag = 相差天数(正)*-1 ,返回负日期
	*(返回负日期是因为使用函数时参数给错了,左小右大)
	*(正常应该是左大右小,这时按照了“-”减号的逻辑实现的)
	*/
}



//“++”运算符重载函数 -- “前置++”:
Date& Date::operator++()
	//“++”对于日期类的意义:计算明天的日期
{
	//直接复用“+=”实现“++”:
	*this += 1; 
	//“++”是会改变左值的,所以使用“+=”
	
	//返回“明天的日期”:
	return *this;
}

//“++”运算符重载函数 -- “后置++”:
Date Date::operator++(int)
{
	//在当前日期加上一天前,先保留当前日期:
	Date tmp(*this); //拷贝当前日期:
	//对当前日期+1天:
	*this += 1;

	//但返回的是+1天前的日期:
	return tmp;

	//使用的是当前日期,实际日期已+1天
	
	/*
	* 因为“++”运算符有“前置++”和“后置++”,
	* 所以需要分别重载实现,重载两次,
	* 但是要让“前置++”和“后置++”构成重载函数,
	* 需要函数名相同,参数不同,但不管是哪种“++”,
	* 都只有一个操作数,隐藏的this指针就够用了,
	* 所以为了区分“前置++”和“后置++”,
	* “后置++”的参数会多一个int(必须是int),
	* 也可以相应的给个实参,如:int i,
	* 但是实现过程不会使用到i,所以一般直接写int即可
	*/

	/*
	* “前置++”和“后置++”对比:
	* “前置++”的效率是比较高的,
	* 因为“后置++”为了实现“后置”需要进行两次拷贝,
	* 所以自定义类型中使用“++”的话,
	* 最好还是使用“前置++”比较好效率高
	*/
}

//“--”运算符重载函数 -- “前置--”:
Date& Date::operator--()
	//“--”对于日期类的意义:计算前天的日期
{
	//直接复用“-=”实现“--”:
	*this -= 1;
	//“--”是会改变左值的,所以使用“-=”

	//返回“前天的日期”:
	return *this;
}

//“--”运算符重载函数 -- “后置--”:
Date Date::operator--(int)
{
	//在当前日期减去一天前,先保留当前日期:
	Date tmp(*this); //拷贝当前日期:
	//对当前日期-1天:
	*this -= 1;

	//但返回的是-1天前的日期:
	return tmp;

	//使用的是当前日期,实际日期已-1天
}

                     

                     


                    

Test.cpp -- 测试文件:

//包含日期类头文件:
#include "Date.h"

//优化全缺省构造函数:
void TestDate1()
{
	Date d1;
	//打印d1日期:
	d1.Print();

	//全缺省构造函数的完善:

	//初始化的日期可能不合法(月份):
	Date d2(2023, 13, 1);
	d2.Print();

	//初始化的日期可能不合法(日期):
	Date d3(2010, 2, 29); //2月可能没有29天
	d3.Print();
}

//测试 “-” 和 “+”(日期-+整型):
void TestDate2()
{
	//创建一个日期类对象:d1
	Date d1(2023, 10, 24);
	d1.Print();

	//测试“-”运算符重载函数:
	Date ret1 = d1 - 100; //隔几个月
	ret1.Print(); //打印结果

	Date ret2 = d1 - 10000; //跨越闰年
	ret2.Print(); //打印结果

	//测试“+”运算符重载函数:
	Date ret3 = d1 + 100; //隔几个月
	ret3.Print(); //打印结果

	Date ret4 = d1 + 10000; //跨越闰年
	ret4.Print(); //打印结果
}

//测试“前置++”和“后置++”:
void TestDate3()
{
	//创建一个日期类对象:d1
	Date d1(2023, 10, 24);
	d1.Print();

	++d1; 
	//d1.operator++() -- “前置++”
	d1.Print(); //2023/10/25


	Date d2 = d1++;
	//d1.operator++(int) -- “后置++”
	d2.Print(); //先取到d1:2023/10/25,再++
	d1.Print(); //2023/10/26
}

//测试“日期-日期”运算符重载函数:
void TestDate4()
{
	//创建一个日期类对象:d1
	Date d1(2023, 10, 24);
	d1.Print();

	//创建一个日期类对象:d2
	Date d2(2024, 2, 10);
	d2.Print();

	//打印两日期相差的日期:
	cout << (d2 - d1) << endl;
	cout << (d1 - d2) << endl;
} 

//测试“+=”的特殊情况:
void TestDate5()
{
	Date d1(2023, 10, 24);
	d1 += -100;

	d1.Print();
}

int main()
{
	//TestDate1();
	//TestDate2();
	//TestDate3();
	TestDate4();
	//TestDate5();

	return 0;
}

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

二 . 流运算符重载函数

“<<”流运算符重载函数

                  

简单介绍<<流插入运算符:

  • << -- 流插入运算符输出流运算符),默认只支持内置类型的调用
    自定义类型默认不能使用流插入运算符所以需要自定义该运算符的重载函数

    operator<<()函数

                          
  • <<运算符两个操作数
    左操作数cout,即ostream类输出流对象
    右操作数:这里以日期类为例,这里是接收一个日期类对象接收后打印该日期

    而在C++中流运算符是可以自动分辨右操作数的类型

    我们所说的自动分辨其实是通过函数重载实现的

    数据类型不同调用的重载函数不同参数匹配不同
                           

  • 本质是为了解决自定义类型输入输出问题
    C语言printf scanf 无法解决自定义类型的输入输出问题
    使用时需要使用%指定类型%d只能指定内置类型
    所以通过 面向对象+运算符重载 来解决该问题

                        

                          
---------------------------------------------------------------------------------------------

                       

实现“<<”流运算符重载函数会遇到的问题:

"<<"左右操作数问题:
  • 在实现运算符重载函数时如果该运算符有两个操作数
    函数的第一个参数会是左操作数而函数的第二个参数会是右操作数
    一般运算符对左右操作数的顺序没有要求"<<"这个运算符有点例外
                      
  • 一般使用时是这样的cout << d1;
    可以看到左操作数会是cout这个输出流对象右操作数日期类对象
                        
  • 如果我们将<<运算符重载函数实现在成员函数中的话
    成员函数默认会有一个隐藏的this指针作为函数的第一个参数且存储的是d1对象
    那么第二个参数就会是输出流对象cout,
    那么左操作数就会是d1对象右操作数就会是cout那么实际调用时就会是这样
    d1 << cout;
    有点违反常理cout流入d1中,可读性差
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                

                

类中私有成员变量使用问题:
  • 所以为了不受制于隐藏的this指针我们需要将"<<"流运算符重载函数实现在全局
    但在全局进行实现的话无法访问类中的私有成员变量
                  
  • 为了在全局也能访问类中的私有成员变量需要将该函数设置成友元函数
    friend友元函数关键字):友元函数可以访问类中的私有成员变量
    (之后会跟细致地讲解友元函数)

                            

                              
    ---------------------------------------------------------------------------------------------

                           

    operator<<函数 -- “<<”流运算符重载函数

                    

  • 该函数需要声明在全局中不受制于隐藏的this指针
    同时为了能够调用私有成员变量需要将其设置为友元函数
                   
  • "<<"还需要支持连续打印,如:cout << d1 << d2 << endl;
    该语句执行时是从左往右执行先执行cout << d1返回cout
    返回的cout再和后面的d2形成cout << d2打印d2后返回cout
    再和后面的endl形成cout << endl进行换行
    注意:
    endl就是\n’,char类型所以执行 cout << endl
    "
    <<"调用的是库中char类型"<<"流运算符重载函数不是日期类
                        
  • 日期类自定义类型但日期类中的成员变量年不是而是内置类型
    所以对这些内置类型正常调用<<打印即可实现日期类对象的打印
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

                     

                     


                    

“>>”流运算符重载函数

              

operator>>函数 -- “>>”流运算符重载函数

                

  • 有了前面“<<流插入运算符输出流运算符)重载函数的实现,
    ">>"输入流运算符重载函数也是同理

             
  • 日期类自定义类型但日期类中的成员变量年不是而是内置类型
    所以对这些内置类型正常调用>>输入即可实现日期类对象的输入
 图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

三 . const成员

回顾 -- const:

const是一个关键字被const修饰的变量不允许被改变静态变量

const修饰一个指针

const指针变量类型的右边修饰的是指针本身指针常量);

const在指针变量类型的左边修饰的是指针所指向的内容常量指针

                

const成员函数

  • 成员函数中隐藏的this指针的类型(以日期类为例):
    Date* const (this)
                    
  • 如果我们使用const对类对象进行修饰那么其类型(以日期类为例):
    const Date (d1)
    如果该对象调用成员函数的话隐藏的this指针会获取该对象的地址&d1),
    所以函数中this指针指向的对象类型就是
    const Date* (d1)
               
  • 所以当对象调用成员函数
    this指针的Date* const (this) 会接收 d1的const Date* (d1),
    此时this指针指针常量const在类型右边),d1常量指针(const在类型左边
    d1常量指针 传到 隐藏this指针常量权限被放大编译器是不允许的
    d1中内容本来是不能改变传到this指针可以改变
                         
  • 权限只能是平移缩小唯独不能放大为了让const对象可以调用成员函数
    需要对成员函数中隐藏的this指针进行const修饰使其类型变成
    const Date* const (this)
    这样当其接收const对象就是 const Date* const (this) 接收 const Date* (d1),
    权限的平移是被允许的
    接收非const对象就是 const Date* const (this) 接收 Date* (d1) ,
    权限的缩小也是被允许的
                 
  • 需要使用const修饰的成员函数
    这里加的const是加到隐藏this指针上成员函数才有隐藏this指针

    被const修饰的对象静态内容无法被修改
    所以当成员函数中隐藏的this指针所指向的对象函数实现过程中不需要被改变
    该成员函数就适合使用const对其进行修饰
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

四 . “&”取地址运算符重载

&取地址运算符也是可以进行重载
上面我们了解了const成员函数这里的&运算符重载函数也需要考虑这个问题

                     

“&”运算符重载函数:

  • &取地址运算符就是获取对象的地址并返回
    对于自定义类型对象就是返回该对象的地址
               
  • 但为了应对const对象需要实现两个版本(以日期类为例):
    Date* const (this) 版本 const Date* const (this) 版本
                      
  • &运算符重载函数非const版本const版本都是默认成员函数
    如果我们不显式定义编译器也会默认生成这两个运算符重载函数
                
  • 虽然可以显式实现显式实现没什么可以操作
    编译器默认生成的功能接近都是直接返回对象地址
    所以这两个重载函数一般不会显式定义(对一般类来讲)
图示:

【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载),CCC全是C,c++,开发语言,visualstudio文章来源地址https://www.toymoban.com/news/detail-752951.html

到了这里,关于【C++初阶】五、类和对象(日期类的完善、流运算符重载函数、const成员、“&”取地址运算符重载)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++——类和对象之运算符重载

    本章思维导图: 注:本章思维导图对应的 xmind 文件和 .png 文件都已同步导入至”资源“ 我们都知道, 对于内置类型我们是可以直接用运算符直接对其进行操作的,但是对于自定义类型,这种做法是不被允许的 。 例如对于 Date 类: 因此, 为了解决自定义类型不能使用操作

    2024年02月05日
    浏览(49)
  • 【c++】类和对象(五)赋值运算符重载

    🔥个人主页 : Quitecoder 🔥 专栏 : c++笔记仓 朋友们大家好,本篇文章带大家认识赋值运算符重载,const成员,取地址及const取地址操作符重载等内容 运算符重载是一种编程语言特性,它允许开发者为已有的运算符提供自定义的实现。这意味着你可以改变某些运算符在你自定

    2024年04月10日
    浏览(45)
  • [C++ ]:5.类和对象中(运算符重载补充)+ 类和对象下(初始化列表)

    我们知道进行运算符重载这个函数的参数的左右类型是非常重要的,我们尝试在类中去定义这个流插入重载! 1. 考虑到隐含的参数指针: 2.进行优化! 我们观察上面的代码发现可以实现在类中进行流插入运算符的一个重载但是我们需要考虑隐含参数的位置所以我们进行传参

    2024年02月06日
    浏览(47)
  • 【C++类和对象】日期类的实现

    hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 💥 个人主页 :大耳朵土土垚的博客 💥 所属专栏 :C++入门至进阶 这里将会不定期更新有关C++的内容,希望大家多多点赞关注收藏💖💖 通过下面的学习我们将构建简单日期计算器的各种

    2024年04月23日
    浏览(51)
  • 【C++】:拷贝构造函数与赋值运算符重载的实例应用之日期类的实现

    🔑日期类的实现,将按以下声明依次进行,其中因为Print函数比较短,直接放到类里面让其变成内联函数 🔑而我们实现日期类经常要用到某月有多少天,在这里先把获得某月有多少天的函数实现出来。实现时先检查传参有没有问题,在注意把数组设置成静态的,出了作用域

    2024年02月08日
    浏览(65)
  • 【C++】类和对象(中)---拷贝构造函数、赋值运算符重载

    个人主页:平行线也会相交💪 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【C++之路】💌 本专栏旨在记录C++的学习路线,望对大家有所帮助🙇‍ 希望我们一起努力、成长,共同进步。🍓 拷贝构造函数,又称复制构造函数,是一种特殊的

    2024年02月05日
    浏览(48)
  • 【C++基础(六)】类和对象(中) --拷贝构造,运算符重载

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C++初阶之路⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习C++   🔝🔝 本章重点: 本篇文章将详细讲解拷贝构造函数 和运算符重载,并介绍const成员的概念 拷贝构造函数和运算符重载 是类和对象中六大默认成员函数

    2024年02月14日
    浏览(42)
  • C++ -3- 类和对象 (中) | 拷贝构造函数 & 赋值运算符重载

    示例: 拷贝初始化构造函数的作用是将一个已知对象的数据成员值拷贝给正在创建的另一个同类的对象 无穷递归 ? Date(Date d){……} 首先,分析 传值传参的过程 传引用传参 : 没有拷贝 的过程,直接传 传值传参: 内置类型 编译器可以直接拷贝(浅拷贝/值拷贝——一个字节

    2023年04月19日
    浏览(80)
  • 【C++】:类和对象(中)之拷贝构造函数+赋值运算符重载

    在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎 那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢? 拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调

    2024年02月06日
    浏览(46)
  • C++类和对象 练习小项目---日期类的实现.

    🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻推荐专栏1: 🍔🍟🌯C语言初阶 🐻推荐专栏2: 🍔🍟🌯C语言进阶 🔑个人信条: 🌵知行合一 🍉本篇简介::为了更好的理解 C++ 类和对象的知识,我们可以动手实现一下 C++ 的一个简单的日期类,完成相应的函数,更好的帮助我们理解类和对

    2024年02月14日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包