类之间的运算
这里可以利用抽象代数的观点来看待这种处理,把运算扩充到任意的集合上,而不是只在数集上.运算可以对应于某个集合下元素之间的关系,可以在运算的世界中表达逻辑.在软件工程观点来看,让代码变得简洁
C++运算符重载:用同一个运算符完成不同的运算功能。
限制
基本原则
C++运算符重载的相关规定如下:
1.不能改变运算符的优先级。
2.不能改变运算符的结合性。
3.默认参数不能和重载的运算符一起使用,也就是说,在设计运算符重载成员函数时不能使用默认函数。
4.不能改变运算符的操作数的个数。
5.不能创建新的运算符,只有已有运算符可以被重载
6.运算符作用于C++内部提供的数据类型时,原来含义保持不变
7. 重载的运算符至少有一个操作数是自定义类型
C++中可被重载的运算符:
不可被重载的运算符
- sizeof
- .: 成员运算符
- .* 成员指针运算符
- :: 作用域运算符
- ?:: 条件运算符
- typeid: 运行时信息运算符
- const_cast/dynamic_cast/static_cast/reinterpret_cast 类型转换运算符
上面这些并不是表达元素之间的运算关系,而是取成员和类型转换,这些由类的元数据决定,当一个类确定之后,这些的运算结果就应该是确定的
只可通过成员函数重载
- = 赋值
- () 函数调用
- [] 下表
- -> 通过指针访问类成员
格式
1 | 函数类型 operator 重载运算符(形参表) |
原理
实际上都是编译器做了特定表达式/类型下运算符->函数的转换.最终都被编译器转换成函数
实例
重载自增自减运算符
1 | #include<iostream> |
重载双目运算符
成员函数
假设有一个类A,对于双目运算符op,如果重载运算符op使之能够实现表达式“obj1 op obj2”,其中obj1和obj2均为A类的对象。
若把op重载为A类的成员函数,该函数只有一个形参,形参的类型是obj2所属的类型。是把op前面的看成对象,op右边的看出函数参数
1 | obj1.operator op(obj2) |
友元函数
假设有一个类A,对于双目运算符op,如果重载运算符op使之能够实现表达式“obj1 op obj2”,其中obj1和obj2均为A类的对象。
若把op重载为A类的友元函数,该函数有两个形参,经过重载之后,表达式“obj1 op obj2”解释为:
1 | obj1 op(obj1,obj2) |
重载赋值运算符
哈哈,这里不就是我们熟悉的拷贝构造函数和赋值运算符.找到理论和实际的结合点了
重载下标运算符
下标运算符“[ ]”通常用于获取数组的某个元素,重载下标运算符可以实现数组下标的越界检测等。下标运算符重载只能作为类的成员函数,不能作为类的友元函数。
重载new delete换运算符
new和delete只能被重载为类的成员函数,不能重载为友元。而且,无论是否使用关键字static进行修饰,重载了的new和delete均为类的静态成员函数。
运算符new重载的一般格式如下:
1 | void *类名::operator new(size_t,参数表); |
使用如下:
1 | x*pX=new(1,2,3) X; |
重载类型转换运算符
这个特性是和拷贝构造函数对应的,比如有一个类Stock可以根据传入的double来构造,
1 | class Stock{ |
那么如果要将Stock转出double呢,就要用到下面的类型转换运算符.这两个函数是一一对应的
格式:
1 | operator 类型名() |
重载函数调用运算符
函数调用运算符“()”只能说明成类的非静态成员函数,该函数具有以下的一般格式:
1 | 函数类型 类名::operator()(参数表) |
重载移位运算符
按照输入流和输出流的概念,如果能有一种运算符可以让对象直接和输入流输出流交互,就不用再写printf(“%s”)这种冗余代码了,还要记类型的format.
于是我们想,把移位看出向输入流和输出流传递数据,正好有左移和右移.开口大的流向开口小的.于是会有下面的代码:
1 | 输出流 << obj |
幸运的是,C++大佬们设计出了这样的特性. 根据友元函数运算符重载的特点,对于双目操作符是将 obj1 op opj2转换成 op(obj1,obj2);
并且这样还可以支持任意一种自定义类型却不用修改std库中输入流和输出流的代码.这实际上也是友元函数重载解决的问题
1 | #include<iostream> |
运算符重载的坑
意外的类型转换
在编程实践中,对于类型转换和拷贝构造函数有可能发生了意外的隐式转换,导致bug,所以最好是加上explict关键字,告诉编译器不要做隐式转换
重载双目运算符不满足交换律
1 | #include<iostream> |
例子中会无法通过编译,因为双目运算符成员函数左边必须是对象,友元重载必须参数类型要匹配(按照前面双目运算符的等价函数调用来理解)。这就导致的加法不满足交换律.
过多的转换函数导致的二义性
假定Stock有形参为double的拷贝构造函数,并且重载了Stock类之间的加法
1 | Stock a(1.0); |
如果此时还又定义了Stock到double的转换函数,那么最后一行将不知道是将a转成double还是将b转成Stock,导致歧义
测试代码见
引用
- C++ Prime Plus
- C++重载运算符实例