一.背景

在复习《C++基础与提高》时,自己实现运算符重载(i++)时,几次都报错。其实还是自己对运算符重载这一部分内容理解得不够透彻,于是再次看了下书上的内容,理解算是加深了一些,于是提笔记录一下。

环境:win10,QT4.8

二.概述

这部分内容主要关于在重载函数中,函数前要不要加const,何时加const,返回类型要不要加&(引用)修饰,何时加&(引用)的问题,还有临时对象的问题。关于为什么要重载,重载的规则,友元重载、成员重载的区别之类的知识点,这里就不赘述了。

三.内容

以类Complex为例

 1 class Complex
2 {
3 public:
4 Complex(double x = 0, double y = 0)
5 :m_x(x), m_y(y){}
6
7 void dis()
8 {
9 cout<<"("<<m_x<<", "<<m_y<<")"<<endl;
10 }
11 protected:
12 double m_x;
13 double m_y;
14 };

1.以实现单目运算符prefix++和surfix++为例。

先提这个例子,一是因为我在复习这块时遇到了一点问题,二是这个有点特别,涉及到哑元的问题。

prefixe++

1).考虑基本数据类型,以int类型为例,如下的操作都是可以的;

1 int a = 1;
2 ++a;
3 ++++a;

2).先实现基本的语义,代码如下:

1 Complex Complex::operator++(void)
2 {
3 m_x++;
4 m_y++;
5 return *this;
6 }

3)考虑添加
重载函数返回的是对象自身,并且需要修改对象,我们即可以想到返回的是引用类型。注意,此时引用指向的对象在重载函数调用时就已经存在了。

4)先运行一下,看下是否能编译通过
++c1;
++++c1;
此时重载函数实现的效果,与基本类型效果一致,符合预期,此时就不考虑重载函数前面是否加const修饰了。

 1 #include <iostream>
2
3 using namespace std;
4
5 class Complex
6 {
7 public:
8 Complex(double x = 0, double y =0)
9 :m_x(x), m_y(y){}
10
11 void dis()
12 {
13 cout<<"("<<m_x<<", "<<m_y<<")"<<endl;
14 }
15
16 Complex & operator++(void);
17 protected:
18 double m_x;
19 double m_y;
20 };
21
22 Complex & Complex::operator++(void)
23 {
24 m_x++;
25 m_y++;
26 return *this;
27 }
28
29 int main()
30 {
31 double a = 1.0;
32 cout<<++a<<endl;
33 ++++a;
34 cout<<a<<endl;
35
36 Complex c1(1.0, 2.0);
37
38 Complex cc = ++c1;
39 cc.dis();
40 cc = ++++c1; // cc = (c1.operator++()).operator++();
41 cc.dis();
42
43
44 return 0;
45 }

结果如下

surfix++

为了区分prefix++和surfix++两个成员函数,须使用哑元进行区分(引入 哑元,增加了入参的方式,在调用时不需要添加任何的参数),其实类似一个占位符。

1).考虑基本数据类型,以int类型为例,可以进行的操作和不可以进行的操作

1 int b = 1;
2 b++; // 支持
3 b++++; // 不支持

2).先实现基本的语义,代码如下

1     Complex operator++(int)
2 {
3 Complex temp = *this;
4 m_x++;
5 m_y++;
6 return temp;
7 }

3)考虑添加

可以观察到,重载函数返回的是一个临时对象。若是串联调用,这个临时对象它又会调用一次此重载函数

c1.operator++(0).operator++(0);

调用完,然后就消失了。

此时切不可在返回类型中添加&。原因如下:
不要返回局部对象的引用或指针
函数完成后,它所占用的存储空间也随之被释放掉。因此,函数终止意味着局部变量的引用将指向不再有效的内存区域。同样地,函数终止,局部对象被释放,指针将指向一个不存在的对象

4)先运行一下,看下是否能编译通过

我们会发现,第34行无法通过编译,但是第42行可以通过编译。

5)重载的运算符是否会导致表达式可以被赋值,应该以基础类型为准,如int a, b, c; (a=b)=c;是可以的,而(a+b)=c;是不允许的。返回类型通过加const加以限定来实现。

为了使自定义类型与基本数据类型一致,我们在返回类型前面加上const。重载函数中代码修改为如下

1 const Complex operator++(int);

修改之后,我们可以看到,第34行和42行均无法通过编译,符合预期。

2.双目运算符+

1)考虑基本类型,以下操作都是支持的

1 int a1 = 1, a2 = 2, a3 = 3;
2 int m;
3 m = a1+a2;
4 m = a1+(a2+a3);
5 m = (a1+a2)+a3;

2)先重载=,成员函数如下

1 Complex & Complex::operator=(const Complex &another)
2 {
3 this->m_x = another.m_x;
4 this->m_y = another.m_y;
5 return *this;
6 }

3)再重载运算符+,如下:

因为并未修改传入的参数,所以参数前加了const

1 Complex Complex::operator+(const Complex &another)
2 {
3 return Complex(this->m_x + another.m_x, this->m_y + another.m_y);
4 }

4)返回类型是否需要加const呢?

我们再对比下表达式的赋值情况,第49行,对于基本类型,临时对象被赋值的情况编译无法通过,但是第58行,自定义类型却编译通过了。此时,为了使其编译不过,可通过在返回值类型前加const加以限定。

将代码

1 Complex Complex::operator+(const Complex &another);

修改为如下:

1 const Complex Complex::operator+(const Complex &another);

5)此时,发现第49和58行均无法通过编译,同时第55行和第57行也编译不过了。

这个是为啥呢?

再仔细看刚修改的代码和第57行代码。重载函数返回类型加了const后,返回的就是const对象了。第57行代码,c1 + c2 + c3; c1 + c2返回的是const对象,而重载函数是一个非const函数。此时,即会报错。

在const修饰类一节中,有学习过:如果const构成函数重载,const对象只能调用const函数,非const对象优先调用非const函数

调整,在重载函数后面添加const,如下:

1 const Complex Complex::operator+(const Complex &another) const;

四.结尾

学无止境,继续前行,

参考材料

《C++基础与提高》  王桂林

《C++ Primer》第5版 SB、JL、BE

最新文章

  1. RabbitMQ学习总结 第七篇:RCP(远程过程调用协议)
  2. JavaScript 跳坑指南
  3. axis
  4. 页面上有两个元素id相同,js中如何取值
  5. php 解决和避免form表单重复提交的方法
  6. hdu 3535 AreYouBusy 分组背包
  7. 蓝牙4.0LED灯控方案
  8. DataTrigger的几个用法
  9. CentOS thrift python demo
  10. Java这点事
  11. Top 10 Mistakes Java Developers Make--reference
  12. Java编程杂记
  13. 【linux驱动】linux驱动总览
  14. PHP通过url下载远程图片到本地
  15. java环境变量和tomcat环境变量配置
  16. Python第二十四天 binascii模块
  17. [Swift]LeetCode154. 寻找旋转排序数组中的最小值 II | Find Minimum in Rotated Sorted Array II
  18. Eclipse 添加 Source 源代码、Javadoc 文档
  19. base64位代码转图片文件并保存到文件夹的解决方案
  20. [No000012A]WPF(2/7):布局,容器和布局转换[译]

热门文章

  1. spring-ioc的注解 理解-1
  2. 后端程序员之路 39、一个Protocol Buffer实例
  3. 用go实现常见的数据结构
  4. css 超过一行省略号
  5. Elasticsearch精进之路:elasticsearch-head插件使用教程
  6. 选择 FreeBSD 而不是 Linux 的技术性原因4
  7. Java数组:初识数组
  8. VScode 自定义用户代码块
  9. Redis不是一直号称单线程效率也很高吗,为什么又采用多线程了?
  10. IDA报错fatal error before kernel init