当前课程知识点:基于Linux的C++ > 第十讲 操作符重载 > 10.13 操作符重载总结 > LinuxCPP1013
我们可以对操作符的重载
进行一些简单的总结
C或C++提供了很多很多个操作符
而且随着C++11的版本的出现
操作符在增加得越来越多
又增加了好多个操作符
哪些操作符可以重载 哪些不可以重载
我们是需要说明的
不可以重载的操作符 同学们要记得
“::”操作符不可以
“?:”对不可以
“.”不可以
“.*”不可以
sizeof不可以
“#”不可以
“##”不可以
typeid不可以
这些操作符是不可以被重载的
剩下的都可以重载
这是指C++11以前的
C++11后来又添了很多操作符
那些就不在我们考虑范围之内
这些不可以重载 其它的都可以重载
那么这些操作符在重载过程中
要遵循什么样的原则呢
正常情况下 我们列出来这些原则
同学们一定要记得遵守
这些原则其实不是特别复杂
只要记住就行了
一个 当你重载操作符的时候
你只能重载已有的操作符
你不能创建新的操作符
对于程序员来讲 没有这个权利
创建新操作符 这是第一个
第二个 你要记得
操作符本身 它实际上也是一个函数
所以重载本身必须遵循我们的
函数重载的原则
函数签名必须是不一样的
重载的操作符不能改变
操作符的优先级和结合性
这一点是不允许你改变的
也不能改变我们这个
操作符的操作数个数和它的语法结构
这都是不能变的
重载的操作符不能改变
它作用于内部类型对象上面的含义
所以它只能和我们
用户自定义的对象一块使用
而且你还要记得
你重载的这个用法实际上还应该
跟过去在功能上是一致的
形式上是统一的
也就是说你要保持重载的操作符的语义
和原始的语义没有变化
保持一致 否则别人很难用
不是说你一定是这样
你如果说 我真地把一个加法操作符
给它重载成一个减法操作
它行不行呢 行
你那么编程 它没问题 编译能通过
调用也能够正确执行
但是a+b的结果给你算出a-b的值来
你让使用你这个类库的那个程序员……
你想想他是什么心情
你就知道 你这么处理
它实际上是毫无道理的
你保持语义不变
这是一个非常重要的原则
应该这么去做的 操作符重载的时候
大部分操作符
都是既可以当成员函数来重载
也可以当友元函数来重载的
你只需要记住
如果是重载为类的成员函数的话
它就隐含着一个this指针
所以名义上它就会少一个参数
实际上它不少
这是非常重要的一个地方
它是少一个参数的
如果你把重载为类的友元函数
因为没有这个隐含的this指针
所以这两个双操作数你都要传
单参数的话 不重载为类的友元函数
我们待会来讨论
成员函数与友元函数的选择
有些基本的原则就是
一般来讲全局的常用的操作符
比如关系操作符、逻辑操作符
像这个流操作符
我们往往重载为类的友元函数
这是全局的操作符
涉及到对象特殊运算的这样操作符呢
哪怕是全局的
我们有时也把它重载为类的成员函数
一般单目操作符重载为成员函数
就不把这个函数的名字公开在外部
形成全局的嘛
你重载为友元函数的话
那个函数往往就是全局的
所以如果单目运算的话
就往往把它重载为一个成员函数
而不是友元函数 双目操作符呢
我们习惯于把它重载为类的友元函数
这是一个更好的方法
这样的话 左操作数和右操作数
可以相当灵活地去设置
使用其实更加方便
有些双目操作符是不能够
重载为类的友元函数的
要特别强调这一点
比如讲赋值操作符 这个不可以
“()”就是函数调用操作符 不可以
“[]”操作符 不可以
那个“->”操作符
那实际上是一个选员操作符
一个指针 引领它的目标结构体的一个成员
那是个引领操作符 选员 对吧
它是不可以的
你不能够把它重载成友元函数
必须隐式地提供它的this指针
这是非常重要的一个地方
类型转换操作符
只能重载为类的成员函数
你不能重载为它的友元函数的
重载的操作符
一般参数采用引用格式传递
主要是为了和我们的数学运算相协调
其实我们前面已经解释过这个问题了
我们现在作为一个总结
我们就把这个例子
给大家演示一下你就知道
如果我定义了两个Couple类的对象a和b
又有一个对象c
然后我把c=a+b这样一个动作
数学上来讲它是一个标准格式
我们就这么写的 把俩数相加 对吧
如果你的定义 重载的操作符定义
返回值是Couple
然后operate+是传递两个Couple类的指针
如果是这样的定义
那么当你调用这个函数的时候
你其实不能那么写
因为左操作数和右操作数
你都必须传指向Couple类的一个对象的指针
所以你应该写&a、&b 对吧
这两个对象在这儿 a和b
你传的是&a、&b 你好崩溃啊
数学上是这么写的吗
不是这么写的
那你可能就会说
我前面就写Couple *a、Couple *b
然后我new它们嘛
恰好这地方不就写c=a+b吗
是的 那样就可以了
但是那就限定了你所有的对象
必须是个指针 必须动态构造
那个好麻烦哪 对吧
那不方便 所以正常的时候
我们写的时候
引用传递的目的就在这里
就是为了处理这个问题的
以与数学运算相协调 就这个意思
所以我们的操作符重载
它的参数要使用引用格式
不管是左值引用还是右值引用
最后几张幻灯片
将给同学们简单地罗列一下
C++操作符重载的函数原型
这是我们推荐的函数原型
你平时操作符重载的时候
应该按照这样的一个模式进行操作
换作其它模式 有的时候不是不可以
但是它并不是特别恰当的方案
普通四则运算 加减乘除余这种模式
包括我们这个标量乘
我都把它列出来了 包括关系运算六个
基本的格式其实是一样的
然后你看到我们这里面有逻辑运算
可以被重载
它是按照像这样的一个格式的
逻辑运算与、逻辑运算或都是双目的
然后逻辑非 它当然是单目的
你看格式其实是不一样的
然后是单目的运算:正号运算、负号运算
取正取负的
还有四个递增递减造作符
前缀递增、后缀递增、
前缀递减、后缀递减
四个基本的格式 按照这个模式
位操作符 位运算
因为我们以前没解释过位运算
同学们知道有它就行了
平时我们不建议重载
还有动态存储管理的操作符
new或delete给我们做动态存储管理的
那么这两个操作符都可以被重载
重载的格式实际上有好几个版本
new不仅有数组版本一个
还有单版本的 实际上单版本有三个
一个是带一个参数
一个是带两个参数的
另外一个还带着两个参数的
尤其是第三种 new版本
它带着一个哑型指针
作为一个基地址的参数
它意味着它的内存分配
将在一个固定的内存区域
而这个内存区域是已经分配好的
然后我们新创建一个对象
就不再真正分配内存
而只是在已经分配好的
就是base指针所指向的
那段内存区域里
为我们新的对象
划定一个存储空间保存
这个我称它为定位创生
创生就是创造出来
把那个对象给它创造出来 就那个意思
在那个固定位置
而固定位置代表内存
以前已经分配好了
不管是动态分配还是静态分配
反正分配好了的 就这个模式
这个new操作符重载
有的时候相当有意义
为什么呢 就是我们的应用程序
我需要频繁地开辟一些小数组
很小 但是很频繁
因为程序语义本身要求这么做
那么频繁从全局里面
去new、delete、new、delete 很讨厌
要从全局堆里面进行分配
效率其实上是低的
在这种时候 我可以在程序运行一开始
就划定一个非常大的存储空间
上来从操作系统中
把这个存储空间给分配过来 要过来
然后我应用程序内部
每次需要分配小数据对象
想返回的时候
就在我这个分配空间里面
一个接着一个地去分配和管理它
固定在同样的小片区域里
这个内存区域里
这个内存区域我们称它为缓冲池
你就可以重载这个new操作符
来处理你应用程序的特定对象的
存储管理的问题
你可以在这个过程中
提升整个程序的效率
你甚至可以为你的应用程序
提供垃圾回收机制 处理这个指针问题
相当重要的一个设计细节
new和delete既可以重载为全局的
也可以重载为类的成员函数
对于大部分情况下
重载为类的成员函数要更安全一些
重载为全局函数
有的时候一旦覆盖了全局的new版本
可能会导致我们的程序
出现难以预料的结果
所以重载的时候需要特别小心
接下来就是赋值操作符
一堆呢 不仅仅是有基本赋值
包括移动语义和拷贝语义的
实际上还有加赋、减赋 对吧
除了加赋、减赋
还有位运算的赋值呢 一堆
下标操作符 还有函数调用操作符
就是我把小括号对(它不是代表着函数调用嘛?)
我也把那个操作符重载一遍
这样的话 这个函数上就有一个operator()
这样的一个函数
就让这个对象可以像函数一样
去运行一下 这是很典型的
参数当然个数可选 想要多少个都可以
没关系
函数调用操作符有时候会很神奇
它会给我们带来很重要的一个结果
就像我们刚才讲的流对象那样
它的操纵符带参数的
那它其实就是一个函子
就意味着它事实上在实现上
就是重载了函数调用操作符的
一个函数对象 它就是一个对象
上面有一个operate<被重载了
就可以像函数一样的运作 就这意思
我们下一讲会实现自己的函子
你可以看到我们怎么实现的
类型转换操作符
可以把这个类转换成另外一个类
那么你就可以提供
这个类型转换的操作符
可以 必须按照这个模式来
它只能重载为类的成员函数
还有逗号操作符 虽然很怪
但是逗号操作符本身是可以被重载的
注意这个 本身是可以被重载的
但是我不建议你这么干
因为它优先级真得很低
而且真得没有必要
接下来是我们的指针与选员操作符
有点讨厌 “&”操作符是取址的
“*”操作符是引领的 这两个简单
然后是这个“*”操作符 还是引领的
两个版本 引领操作符双版本
这三个简单 后面有一个“->”操作符
还是选员操作符
看上去也不那么复杂 对吧
返回的是那个指针
选员操作符也是两个版本
引领操作符俩版本 选员操作符俩版本
我们总共是5个版本
还有一个很怪异的“->*”操作符
它一样是一个选员操作符
但它比较特殊 它是什么呢
它是指向类成员的一个指针
在我们前一讲讲类与对象的时候
我们根本就没有讨论这个问题
因为这个技术细节太偏了
当我们不需要使用它的时候
我真地不能去介绍它
介绍它 同学们会觉得太奇怪
不知道它干嘛用的 我们后面有例子
一旦你见到了使用它的例子
你就会知道我们为什么需要它
那个时候再去处理这个问题
你对它认识就会很深刻 就会明白
最后就是我们的流操作符
两个输入输出流
我们的插入符、提取符
按照这个模式重载它们就可以了
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口