类和对象3(日期类实现)
函数内要使用类内的成员变量时不用再写形参类型,直接用就行。
若如下右图写,则是构造函数里声明了局部变量_year/_month/_day,覆盖了类的成员变量,导致成员变量未被初始化。
函数声明与定义分离
.h中函数 类型 函数名(参数列表)
.cpp中函数 类型 头文件中类名::函数名(参数列表)
思考:默认构造需要自己写(初始化),析构函数用默认(无资源释放)。拷贝构造,赋值重载用默认(无资源释放)
首先写构造函数初始化一个日期类,再写一个打印函数
再完成日期的加减比较
设计一个函数要求传入年月后得到对应月份的天数(需判断是否是闰月年及二月天数)
四年一润并且百年不润或者400年一润
该数组常用,设置为静态数组。assert对月份进行限定。该函数常用,设置为类内内联函数。
一、 实现+= + -= -
1.+=
不断在自身上加不断改变自身,所以.h中返回类型是Date &
核心思想:_day+day - 当前月天数=当前天数
用总天数按月扣天数,直至所剩天数小于当前月天数
这里的if(day>0)是传入负数时,+=负数等于-=正数
+ 有两种方法
2. -=
不断在自身上加不断改变自身,所以.h中返回类型是Date &
用当前月天数-要回溯的天数。
若>0,说明回溯后还在本月
若<=0,说明回溯后不在本月,当前月份天数以被减完,借上一月份的天数继续减,直至>0
这里的if(day<0)-=负数等于+=正数
- 有两种方法
二、实现比较大小
先实现比较> ==或 < == 就可以轻松实现其它比较逻辑了
这里实现了< ==
这里将<的情况按先年与年,后月与月,最后日与日都挑选出来了,其他>的情况直接 return false
再实现其他
三、实现前置后置++ --
返回类型依旧是Date&(返回更改后本身) 或Date(传值传参),返回更改后的对象,参数为空
后置时在参数位置写int用来区分前置,int无具体含义不传参
三、日期-日期有意义,算出相差天数
算年月日与年月日之间的差值即相差天数,直接算就行
实际d1 d2谁大谁小我们不知道,设计算法找出d1 d2中大的和小的,让小的不断加至大的,其中加了多少次,就隔了多少天,n始终为正数。实际d1 d2相减结果可能为负数,flag就可以实现对应情况
四、检查月日是否正确
加在构造函数后判断输入日期是否合理
五、若想实现输入任意两个日期,相减即可他们之间相隔天数
需要重载<< >>
<< >>都是是双目运算符,左侧操作数是cout
在库内分别已经将<< 和 >>的运算符重载函数的函数重载(如下图以<< 为例)实现完成,这就是c++可以自动识别要输入输出的内置类型的原因
我们将<< >>重新重载(底层实现时还是用到他们原来的重载)
实现<<
将运算符函数放在日期类Date里面(写成成员函数),这样访问私有成员变量更方便。我们按重载函数中隐藏的this指针指向的是调用的日期类,将cout(是对象)引用传参的准则,设计重载函数。将运算符函数放在日期类Date里面,函数设计如下图void Date::operator<<(ostream& out)。有问题,按原来的输入习惯cout<<d1调用时,隐藏的this与形参out参数不匹配,但如果将调用对象交换,就行,但不理想。
此时将该函数放到.h文件的全局域中再加上日期类d,变成全局函数,即可符合使用习惯。在.cpp中完成该函数,但此时无法方便的使用日期类内的成员函数及变量
需在类内加上友元声明(在类内写friend 函数),在类外声明函数,即可在类外访问私有成员变量
此时仅能单次输入,没有返回out形成链式输入
如何让<<连续输入?
<<和赋值运算符=相似(赋值运算符=是从右往左,返回左操作数),返回值是左操作数,即cout,从左往右,所以将函数类型再改为ostream&,即可
实现>>
其他与实现<<原理一样(内部还是由原来的<< >> 实现,全局函数,需要友元,需要链式实现),因为要往对象内存数据,所以去掉const。
istream与ostream类型对象不支持拷贝构造,不能传值传参,传值返回。只能传引用传参,传引用返回。
将>>加入输入数据是否合法的Check判断,如果一直输入错误就一直提示(while循环,break跳出循环)
>>读取整型,空格 / 换行天然充当分隔符,用来区分年、月、日三个数字。
竞赛有要求时
如下图,d1调用不受const限制的成员函数,相当于要将受const修饰的Date类地址传给不受const修饰的隐藏形参指针变量this,应为this不受const修饰,所以可以通过this修改对象内成员,会将其权限放大,
此时可以改函数内部的私有成员变量,如下图++_year
建议在函数内部不需要修改成员变量的成员函数都加上const
。
当我们想返回错误指针,空的地址时
以下三图代码一体
对于A aa2=2; 这行代码,发生了隐式类型转换。因为A的构造函数是一个支持单参数的构造函数,所以语法上编译器先用常量2构造一个临时对象,再用临时对象拷贝构造给aa2,相当于构造对象A aa2(2)。编译器实际优化编译时不调用构造临时对象,再调拷贝构造,而是用常量2直接构造对象aa2。
下图,代码st.Push(3)中,用3隐式类型转换构造了一个临时对象A aa2(3)。然后这里再
没有发生类型转换,只是要对临时对象A aa2(3)进行引用传参(重命名),临时对象由于有常性,所以必须加const。由下图压栈时知以后不用先创建对象再将对象传参,而是直接传创建对象时的要初始化的数据,让编译器自动创建对象,这样写起来更简单。注意形参要有const,只有单参数时支持这样写,多参数不支持
当A的构造函数是一个支持多参数的构造函数,与支持单参数的构造函数时相似,只不过多个数据要用{}括起来,
若不想让隐式类型转换发生,在对应构造函数前加关键字explicit即可
