运算符重载¶
本章大纲¶
- 堆内存分配
- 运算符重载简介
- 运算符重载的限制
- 类成员函数和全局函数的运算符函数比较
- 重载
++
和--
运算符 - 重载
<<
和>>
运算符 - 类型转换
堆内存分配¶
new¶
语法:指针变量 = new <数据类型>[长度];
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
- 如果分配的空间长度为1个单位,那么:
C++ | |
---|---|
1 2 |
|
- 使用
new
分配内存时,其空间长度可以是**变量**或**数值表达式**
C++ | |
---|---|
1 2 |
|
- 由
new
分配的内存空间是连续的
C++ | |
---|---|
1 2 3 |
|
- 如果当前存储器无足够的内存空间可分配,则
new
运算符返回0(NULL)
delete¶
语法:delete mem_ptr_allocated_by_new;
C++ | |
---|---|
1 2 |
|
- 用
new
运算符获得的内存空间,只许使用一次delete
- 不允许对同一块空间进行**多次释放**,否则将会产生严重错误
delete
只能用来释放由new
运算符分配的动态内存空间,对于程序中的变量、数组的存储空间,不得使用delete
运算符去释放
运算符重载¶
- 运算符重载的实质:函数重载,只不过它重载的是类似
+ - * / =
这样的操作符 - C++不但提供了**函数重载**,而且提供了**运算符重载**
- 注意: 运算符被重载后,其原有的功能仍然保留
运算符重载语法:
C++ | |
---|---|
1 2 3 |
|
实例:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
注意事项¶
1. 能否重载¶
1、可以重载的运算符
C++ | |
---|---|
1 2 3 4 5 |
|
2、不可以重载的运算符
C++ | |
---|---|
1 2 3 4 5 6 |
|
2. 重载原则¶
-
**优先级**不变;
-
**结合性**不变;(运算符应用的顺序)
-
操作数个数不变;(元数)
-
**语法结构**不变。
3. 实质:函数重载¶
编译器选择重载的运算符是**遵循函数重载的选择原则**,即按不同类型或个数的参数来选择不同的重载运算符
4、运算符重载应符合使用习惯,便于理解
5、运算符重载**不能创造新的运算符号**
6、用于类对象的运算符一般必须重载,但有两个例外,赋值运算符=
和取地址运算符&
不必用户重载
①赋值运算符(=),可以用于每一个类对象,可以利用它在同类对象之间相互赋值。
② 地址运算符&,也不必重载,它能返回类对象在内存中的起始地址。
类成员函数和全局函数的运算符函数对比¶
-
在C++中,运算符重载是通过**运算符重载函数**实现的,运算符重载函数一般采用下述两种形式之一。
-
成员函数的形式
-
友元函数的形式
-
**运算符重载函数**是一种特殊的成员函数或友员函数(全局函数通常被指定为友元函数)
-
区别
-
成员函数具有 this 指针,友员函数没有this指针
-
不管是成员函数还是友员函数重载,运算符的使用方法相同
-
传递参数的方式不同,实现代码不同,应用场合也不同
运算符重载函数是类成员函数¶
重载语法:
C++ | |
---|---|
1 2 3 4 5 6 |
|
参数表:
- 单目运算参数表中无参数,调用该函数的对象为操作数。
- 双目运算参数表中有一个参数,调用该函数的对象为第一操作数,参数表中的参数为第二操作数。
- 当一元运算符的操作数,或者二元运算符的**左操作数**是类的对象时,定义重载算符函数为成员函数 ,例如
+、-、*、/、% 、=、+=、-=(二元操作)
- C++不能用友元重载的运算符: ()、[]、->或者任何赋值运算符
显式调用和隐式调用
- 当运算符的重载为**成员函数形式**时可采用**隐式**和**显式**两种方式调用
C++ | |
---|---|
1 2 3 |
|
- 一元运算符(@表示已重载运算符)
- 隐式调用:
@对象名
或者对象名@
- 显式调用:
对象.operator@( )
- 二元运算符
- 隐式调用:
对象名1@对象名2
- 显式调用:
对象名1.operator@(对象名2)
重载++和--¶
重载函数只能从形式参数上加以区别
-
运算符前置自增运算符:用成员函数实现时,没有形式参数。
-
运算符后置自增运算符:另外增加一个形式上的形式参数,类型定为int。这个参数只是用来区别两种自增算符,并不参加实际的运算
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
运算符重载函数是友元函数¶
重载语法:
C++ | |
---|---|
1 2 3 |
|
参数表:
- 单目运算符重载,参数表中只有一个形参数;
- 双目运算符重载,参数表中有两个形参数。
- 运算符重载为成员函数和友元函数形式的主要区别在于前者有this 指针,后者无this 指针。
显式调用和隐式调用
- 二元运算符
- 显式:
operator@(对象名1,对象名2)
-
隐式:
对象名1@对象名2
-
一元运算符
- 显式:
operator@(对象名)
-
隐式:
@对象名
或者对象名@
-
重载为友元函数,比如“+”,操作数都由形参给出,通过运算符重载的函数进行传递。并且运算结果的类型与操作数的类型一致。
- 重载运算符的操作中,无论重载为成员函数还是友元函数,其**形参多为引用类型**,目的是增加可读性,提高程序的运行效率,因为使用引用类型,在进行参数传递的过程中,不需要复制临时对象。
- 如果左操作数必须是一个不同类的对象,那么该运算符函数必须作为全局函数来实现(友元函数)。例如运算符
<<
和>>
重载<<和>>¶
- istream 和 ostream 是 C++ 的预定义流类,cin 是 istream 的对象,cout 是 ostream 的对象
- 运算符 << 由ostream 重载为插入操作,用于输出基本类型数据
- 运算符 >> 由 istream 重载为提取操作,用于输入基本类型数据
- 用友员函数重载 << 和 >> ,输出和输入用户自定义的数据类型
C++ | |
---|---|
1 2 3 |
|
数据类型转换¶
在程序编译时或在程序运行实现:
- 基本类型<---->基本类型
- 基本类型<---->类类型
- 类类型<---->类类型
C++数据类型转换方式:
- 隐式数据类转换
- 显式数据类型转换,也叫强制类型转换
对于自定义类型和类类型,类型转换操作是没有定义的。
**转换构造函数**的作用是将一个其他类型的数据转换成一个类的对象。转换构造函数只有一个形参
用户可以根据需要定义转换构造函数,在函数体中告诉编译系统怎样去进行转换。
使用转换构造函数将一个指定的数据转换为类对象的方法如下:
- ① 先声明一个类
- ② 在这个类中定义一个**只有一个参数的构造函数**,参数的类型是需要转换的类型,在函数体中指定转换的方法
- ③ 在该类的作用域内可以用以下形式将指定类型的数据转换为此类的对象:
类名(指定类型的数据);
C++ | |
---|---|
1 2 3 4 |
|
不仅可以将一个标准类型数据转换成类对象,也可以将另一个类的对象转换成转换构造函数所在的类对象。
类型转换函数(类独有)¶
- 用转换构造函数可以将一个指定类型的数据转换为类的对象;不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据)。
- C++提供类型转换函数(type conversion function)来解决这个问题。类型转换函数的作用是将一个类的对象转换成另一类型的数据
- 声明语法:
operator 类型名 () ;
- 特点:
- 无返回值
- 功能类似强制转换
- 只能是成员函数,不能是友元函数或普通函数,因为转换的主体是本类的对象
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
对于r3=r1+2.40;
的系统工作
1、寻找重载的成员函数+运算符
2、寻找重载的友元函数+运算符
3、寻找转换运算符
4、验证转换后的类型是否支持+运算。
转换运算符重载一般建议尽量少使用。
explicit构造函数¶
- 放在单参数的构造函数中,防止隐式转换, 导致函数的入口参数, 出现歧义.
- 如果可以使用A构造B, 未加explicit的构造函数, 当使用B进行参数处理时, 就可以使用A, 使得接口混乱.
- 为了避免这种情况, 使用explicit避免隐式构造, 只能通过显式(explicit)构造.
- 按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象
C++ | |
---|---|
1 2 3 4 5 |
|
有的时候可能会不需要这种隐式转换,如下:
C++ | |
---|---|
1 2 3 4 5 |
|
下面两种写法比较正常:
C++ | |
---|---|
1 2 |
|
下面两种写法就比较疑惑了:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
下面两种写法仍然正确:
C++ | |
---|---|
1 2 |
|
下面两种写法就不允许了:
C++ | |
---|---|
1 2 |
|
explicit 可以有效得防止构造函数的隐式转换带来的错误或者误解.
总结:explicit 只对单参构造函数起作用,用来抑制隐式转换
本章总结¶
- 注意运算符重载的规则和限制
- 重载运算符的时候要注意函数的返回类型
- 前增量和后增量运算符的重载区别
- 赋值运算符重载要注意内存空间的释放和重新申请。
- 转换运算符重载与构造函数、析构函数一样没有返回值,通过转换运算符重载可以在表达式中使用不同类型的对象,但要注意转换运算符重载不可滥用。