今天来学习C++中的前置操作符(++i)与后置操作符(i++)。
一回顾
以前在C语言的理论课程中,我们就学过,前置和后置操作符,有一定的区别:
但是我们可以看到下图,发现前置和后置++的操作符所对应的汇编语言(14行和16行所对应的的汇编完全一样),是完全一样的(在linux下的eclipse软件中进行Debug调试):
这是为什么呢?难道之前学的C语言的理论是错的么?并不是的,这里面,首先这两个i经过前后置++后,并没有被用起来,结果是什么也并不重要,所以现代的编译器软件就对程序进行优化,将这两行语句优化成一样的:
但是这也产生了一些意想不到的事:
- 优化使得最终的二进制程序更加高效
- 优化后的二进制程序丢失了C/C++原有的语义
- 不可能从编译后的二进制程序,还原C/C++程序。
二拓展
问题:
那么,++操作符可以被重载么,能够重载的话,又是如何区分前置++与后置++的?
回答:
++操作符可以被重载
- 全局函数和成员函数都可以被重载
- 重载前置++操作符不需要额外的参数
- 重载后置++操作符需要一个int类型的占位参数
下面我们还是来直接写一个代码,来分析,会更加清晰:
#include <iostream>
#include <string>
using namespace std;
class Test
{
int mValue;
public:
Test(int i)
{
mValue = i;
}
int value()
{
return mValue;
}
Test& operator ++ () //这里返回的是对象的引用,可以对比下面后置++的返回值
{
++mValue; //前置++操作符效率高一些,因为没有生成额外的对象,就不需要在栈空间
//占用额外的空间,节省了内存,并且不需要调用构造函数以及析构函数
return *this; //返回当前对象的指针
}
Test operator ++ (int) //这里为什么返回的不是引用呢?因为后置++是先需要把值保存起来然后再自加一,所以这里需要一个局部对象,把
{ //值保存在这个
Test ret(mValue); //先保存值
mValue++; //临时局部的对象中,所以返回值就是类对象的值。再++
return ret;
}
};
int main()
{
Test t(0);
Test tt = t++;
cout << tt.value() << endl;
cout << t.value() << endl;
cout << endl;
Test tt2 = ++t;
cout << tt2.value() << endl;
cout << t.value() << endl;
return 0;
}
运行结果为:
0
1
2
2
分析:
程序注释中,对于操作符的重载已经讲解的很清楚。
由打印结果知,前两个值为0,1,后置操作符先把值给tt,再自己++,这保持了后置操作符的原生语义。同理,由后两个打印语句,得知前置操作符的原生语义,也被保持了。
那么,这两个重载的操作符,真正的区别,在哪里呢?
-对于基础类型的变量
- 前置++与后置++的操作符的效率基本相同
根据项目组编码规范进行选择
-对于类类型的对象
前置++的效率高于后置++的效率(具体原因看程序注释)
- 尽量使用前置++的操作符提高程序的效率
3延伸
那么我们现在就可以来再次完善我们之前实现的复数类了,具体代码整个代码过长,我就放到码云平台,大家可以自行下载。
点击下载:点击下载复数类源码
4总结
- 编译优化使得最终的可执行程序更加高效
- 前置操作符++与后置操作符++都可以被重载
- ++操作符的重载必须符合其原生语义
- 对于基础类型,前置和后置++操作符效率几乎一样
- 对于类类型,前置++的效率高于后置++的效率
想一起探讨以及获得各种学习资源加我(有我博客中写的代码的原稿):
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题。