当前课程知识点:C++语言程序设计进阶 > 第八章 多态性 > 运算符重载 > 单目运算符重载为成员函数
如果要重载 U 为类成员函数,使之能够实现表达式 U oprd,其中 oprd 为A类对象,则 U 应被重载为 A 类的成员函数,无形参。
经重载后,表达式 U oprd 相当于 oprd.operator U()
如果要重载 ++或--为类成员函数,使之能够实现表达式 oprd++ 或 oprd-- ,其中 oprd 为A类对象,则 ++或-- 应被重载为 A 类的成员函数,且具有一个 int 类型形参。
经重载后,表达式 oprd++ 相当于 oprd.operator ++(0)
前置单目运算符,重载函数没有形参
后置++运算符,重载函数需要有一个int形参
操作数是时钟类的对象。
实现时间增加1秒钟。
源代码: #include <iostream> using namespace std; class Clock {//时钟类定义 public: Clock(int hour = 0, int minute = 0, int second = 0); void showTime() const; //前置单目运算符重载 Clock& operator ++ (); //后置单目运算符重载 Clock operator ++ (int); private: int hour, minute, second; }; Clock::Clock(int hour, int minute, int second) { if (0 <= hour && hour < 24 && 0 <= minute && minute < 60 && 0 <= second && second < 60) { this->hour = hour; this->minute = minute; this->second = second; } else cout << "Time error!" << endl; } void Clock::showTime() const { //显示时间 cout << hour << ":" << minute << ":" << second << endl; } 例8-2重载前置++和后置++为时钟类成员函数 Clock & Clock::operator ++ () { second++; if (second >= 60) { second -= 60; minute++; if (minute >= 60) { minute -= 60; hour = (hour + 1) % 24; } } return *this; } Clock Clock::operator ++ (int) { //注意形参表中的整型参数 Clock old = *this; ++(*this); //调用前置“++”运算符 return old; } 例8-2重载前置++和后置++为时钟类成员函数 int main() { Clock myClock(23, 59, 59); cout << "First time output: "; myClock.showTime(); cout << "Show myClock++: "; (myClock++).showTime(); cout << "Show ++myClock: "; (++myClock).showTime(); return 0; }
大家好
欢迎回来继续学习
C++语言程序设计
接下来呢
我们再来看看单目运算符
怎么重载
还有这个前置++
后置++怎么区分呢
那么如果我们要重载一个
单目运算符
比如说我们用代号U表示
某种单目运算符
需要将这样的单目运算符
重载为类的成员函数
那它又分前置单目运算符
和后置单目运算符
首先呢
我们来看
前置单目运算符的重载规则
那么前置单目运算符
它要实现的表达式呢
就是运算符在前
操作数在后
比如说
我们这里用U oprd
来代表
其中这个oprd是A类的对象
那么这个时候
我们可以将U
重载为A类的成员函数
这个时候呢
是不需要形参的
因为只有一个操作数
这个操作数就是类的对象
这个时候如果我们去
在程序中写U oprd
这样的表达式
就相当于通过oprd这个对象
去调用operatorU这个函数
那么如果我们要为后置的++
和--
重载运算符函数
它与前置++ - -的运算符函数
差别在哪儿呢
我们知道
如果说两个函数的名字相同
要能够区分它们的话
那只能用参数表来区分了
因为不管前置++ 后置++
它的运算符都是两个+号
那么这就决定了
它的重载函数名
都必须是operator++
也就说函数名是一样的了
这个时候要区分哪个是前置的
哪个是后置的
当然只能通过参数表
所以呢
后置的自增自减运算符
它的重载函数的参数表
里面的参数
要比前置的多一个参数
而多的这个参数呢
就只用来区分
它是一种不同的重载形式
在函数体中是不能使用它的
所以当我们在程序中
出现了后置++的
这种表达式的时候
就相当于通过对象
去调用operator++函数
但是实参给了它一个整数0
下面这个例子呢
就通过时钟类来演示了
如何将前置++和后置++
都重载为时钟类的成员函数
前置的单目运算符重载的时候
函数是没有形参的
这个我们刚刚知道了
而后置的运算符呢
重载函数
需要有一个int类型的形参
那么在这道例题中呢
操作数就是时钟类的对象
clock类的对象
时间的功能不管是前置++
还是后置++
我们都要求它实现
时间加1秒钟的功能
我记得大家在一开始
学习基本的算术运算的时候
有很多同学
一直存下了一个疑惑
这个前置++和后置++
它到底是怎么实现的这个差别呢
而且有的同学可能甚至就
到底也弄不清楚
它到底差别在哪儿
通过这个例题
通过我们自己写一个函数
来分别实现前置++
写另一个函数实现后置++
这样呢
我们就会彻底地明白
到底这两者实现上
有什么不同
现在我们来看一下
这个例题的原代码
现在我们在时钟类中
重载了两个自增运算符函数
第一个是前置自增的运算符
所以呢
它是参数表中是没有参数的
因为是单目前置运算符
那第二个是后置++运算符
为了区分前置与后置
那么它们的参数表必须不同
所以语法上规定
当重载后置自增运算符的时候
参数表里面
要有一个int类型的参数
但是这个参数
它的目的只是为了区分
它是一个后置的运算符
那么在实现的时候
函数体中
是不应该去使用这个参数的
好 那这个钟表类的定义呢
我们回顾一下
首先它的构造函数中
要判断数据的合法性
然后呢
用参数值去初始化
当前对象的数据成员
然后showTime显示时间
现在看我们刚刚增加的
这个运算符重载函数
先看前置自增运算符
它实现加1秒的功能
那么时间加1秒以后呢
要判断是不是秒数大于等于60了
如果是的话呢
秒数减掉60分加1
如果分钟大于等于60分钟了
那么这么
minute就减60
然后小时数加1
因为用的是24小时制
所以小时数加1以后
还要对24取余
那最后呢
返回当前对象
那么这个前置自增呢
它的运算结果
也就是它的函数的返回值
应该是一个本类对象的引用
也就是说
它返回的是个左值
我们知道前置自增是先增1
后使用
所以使用的就是它返回的那个值
当然得返回它自己
所以应该是个引用
那么再看这个后置加1
后置加1呢
我们说是先使用后加1
这是一个
不太准确的形象的说法
在第二章中
我们讲前置 后置
自增自减运算符的时候呢
这样简单地解释
大家可能心里还有些疑问
什么叫先加1后使用
什么叫先使用后加1呢
到底是怎么实现的呢
肯定做这个自增运算符的时候
它都加1了
那现在我们来看
我们自己写一个实现
后置自增运算的函数
大家就清楚了
那么现在呢
我们要首先将当前对象目前的值
也就是*this
当前对象目前的值
暂时存储在
一个临时的局部变量里面
这个old
然后对当前对象实行加1的运算
那么这个加1
当然我们把上面这段程序
拷贝过来
但是这个不是好办法
比较好的办法就是说
我们直接调用这个前置自增运算符
这样的话呢
都是加1
两边的算法就能够统一了
如果你两个函数体都是单独写的
那么它的算法有可能
在几经修改以后
就变得不一致了
比如说
现在我们使用24小时制的
你可以把代码复制过来
可是后面经过修改
可能想改成12小时制
但是这两处有一处忘了改
那么它就实现功能的方式
就不一致了
所以为了避免这种情况呢
我们比较好的方案就是
让这个后置自增运算
直接调用这个
前置的自增运算函数
所以将当前对象前置++自增1
然后
这个函数的返回结果是什么呢
关键在这儿
返回的结果呢
是刚才存起来的这个
原来没有加1的时间
存在old里面了
将old返回
所以现在我们看到
这两个函数的差别吧
这个返回的是加1以后的那个数
所以呢
这个返回值
再继续参与其他更复杂范围
更大范围的运算的时候
用的是加1以后的那个值
而这个函数返回的是
没有加1的那个旧的值
所以它再去参与
更大范围的运算的时候呢
用的是那个没有加1的旧的值
但是其实这时候
它自己已经暗地里加了1了
这就是前置加1和后置加1
它实现上的差别
现在大家可以理解
一个前置自增运算符
我们说它先加1再使用
那么后置自增运算符
它先使用加1
其实这个说法不是很确切
其实使用的时候
它已经被加1了
只不过使用的是
它没加1之前
就复制出来的那个副本
所以实现的原理是这样的
还有呢
由于这个后置+1
它使用的是
这个对象的一个副本
而不是这个对象自己
所以它返回的是一个右值
所谓右值就是通过这个值
你是不能去改变这个对象的
因为它返回的就是一个复制品
你通过这个复制品
你用这个值没有问题
你触及不到这个对象本身
而前置++呢
它返回的就是对象本身
那么返回值
是当前对象的这个引用
所以通过这个引用
你可以直接操纵这个对象的
所以这是前置++和后置++
它的另一个不同点
就是它的运算结果
前置的它是一个左值
后置的自增
它的运算结果是一个右值
实际上
就是它原来对象的一个副本
现在呢
我们来看在主函数中验证一下
构造了一个时钟类对象
把时间初始化在23点59分59秒
然后我们显示一下这个时间
在这儿调用showTime显示一下
接下来呢
就调用后置自增运算
实际上就调用了这个operator
后置自增这个函数
调用完了以后
大家看这个返回的结果
就应该放在这个括号里面了
通过这个返回的结果
去调用showTime
而这个返回结果
是myClock自己吗
绝对不是
我们看到返回的结果
是myClock自己的一个副本
所以通过这个临时的副本
调用showTime
看到的时间是什么呢
是它还没有加1之前的时间
因为它还没有加1之前的时间
放在旁边存着
然后把这个副本返回了
它自己加1了
但是并没有把加1的这个值返回
所以呢
我们在这里看到的
是没有加1之前的这个数据
接下来呢
我们再调用前置++运算
那么这回呢
基础是什么
基础是
这个原来的23点59分59秒
已经经过后置++增了1秒
变成0点0分0秒了
在这个基础上再加1
而且返回的
就是加1以后的这个结果
所以两次加1的效果
都反映在这次输出里面了
我们看到输出的是0点0分1秒
因为它返回的
是加1以后的自己
所以通过这个例题
我想大家不仅学会了
编写前置自增
后置自增的这个运算符重载函数
也对前置和后置的运算符
有了更清楚地理解
-导学
--导学
-继承的基本概念和语法
-第七章 继承与派生--继承的基本概念和语法习题
-继承方式
-第七章 继承与派生--继承方式
-基类与派生类类型转换
-第七章 继承与派生--基类与派生类类型转换
-派生类的构造和析构
--派生类的构造函数
--派生类的析构函数
--第七章 继承与派生--派生类的构造和析构
-派生类成员的标识与访问
--虚基类
-第七章 继承与派生--派生类成员的标识与访问
-小结
--小结
-综合实例
--第七章综合实例
-实验七
--实验七
-导学
--导学
-第八章 多态性--导学
-运算符重载
--运算符重载的规则
-第八章 多态性--运算符重载
-虚函数
--虚函数
--虚析构函数
--虚表与动态绑定
-第八章 多态性--虚函数
-抽象类
--抽象类
--第八章 多态性--抽象类
-override与final
-第八章 多态性--override与final
-小结
--第八章小结
-综合实例
--第八章综合实例
-实验八
--实验八
- 第八章讲义
-导学
--导学
-模板
--函数模板
--类模板
-第九章 模板与群体数据--模板
-线性群体
--线性群体的概念
-第九章 模板与群体数据--线性群体
-数组
--数组类模板
-链表
--链表类模板
-第九章 模板与群体数据--链表
-栈
--栈类模板
--栈类模板课后习题
--例9-9 栈的应用课后习题
-队列
--队列类模板
-第九章 模板与群体数据--队列
-排序
--排序概述
--插入排序
--选择排序
--交换排序
-第九章 模板与群体数据--排序
-查找
--查找
--查找课后习题
-小结
--小结
-综合实例
--综合实例
-实验九
--实验九
- 第九章讲义
-导学
--导学
-泛型程序设计及STL的结构
--STL简介
-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构
-迭代器
--迭代器
-第十章 泛型程序设计与C++标准模板库--迭代器
-容器的基本功能与分类
-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类
-顺序容器
--顺序容器的特征
--第十章 泛型程序设计与C++标准模板库--顺序容器
-关联容器
--集合
--映射
-第十章 泛型程序设计与C++标准模板库--关联容器
-函数对象
--函数对象
--函数适配器
-算法
--算法
-小结
--第十章小结
-综合实例
--综合实例
-实验十
--实验十
- 第十章讲义
-导学
--导学
-I/O流的概念及流类库结构
-第十一章 流类库与输入/输出--I/O流的概念及流类库结构
-输出流
--输出流概述
--向文本文件输出
--向二进制文件输出
--向字符串输出
-第十一章 流类库与输入/输出--输出流
-输入流
--输入流概述
--输入流应用举例
--从字符串输入
-第十一章 流类库与输入/输出--输入流
-输入/输出流
--输入/输出流
-第十一章 流类库与输入/输出--输入/输出流
-小结
--小结
-综合实例
--综合实例
-实验十一
--实验十一
- 第十一章讲义
-导学
--第12章导学
-异常处理的思想与程序实现
-第十二章 异常处理--异常处理的思想与程序实现
-异常处理中的构造与析构
-第十二章 异常处理--异常处理中的构造与析构
-标准程序库异常处理
-第十二章 异常处理--标准程序库异常处理
-小结
--第12章小结
-综合实例
--综合实例
-实验十二
--实验十二
- 第十二章讲义