今天来学习C++中的前置操作符(++i)与后置操作符(i++)。

一回顾

以前在C语言的理论课程中,我们就学过,前置和后置操作符,有一定的区别:

但是我们可以看到下图,发现前置和后置++的操作符所对应的汇编语言(14行和16行所对应的的汇编完全一样),是完全一样的(在linux下的eclipse软件中进行Debug调试):

这是为什么呢?难道之前学的C语言的理论是错的么?并不是的,这里面,首先这两个i经过前后置++后,并没有被用起来,结果是什么也并不重要,所以现代的编译器软件就对程序进行优化,将这两行语句优化成一样的:

但是这也产生了一些意想不到的事:

  1. 优化使得最终的二进制程序更加高效
  2. 优化后的二进制程序丢失了C/C++原有的语义
  3. 不可能从编译后的二进制程序,还原C/C++程序。

二拓展

问题:
那么,++操作符可以被重载么,能够重载的话,又是如何区分前置++与后置++的?

回答:
++操作符可以被重载

  1. 全局函数和成员函数都可以被重载
  2. 重载前置++操作符不需要额外的参数
  3. 重载后置++操作符需要一个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总结

  1. 编译优化使得最终的可执行程序更加高效
  2. 前置操作符++与后置操作符++都可以被重载
  3. ++操作符的重载必须符合其原生语义
  4. 对于基础类型,前置和后置++操作符效率几乎一样
  5. 对于类类型,前置++的效率高于后置++的效率

想一起探讨以及获得各种学习资源加我(有我博客中写的代码的原稿):
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题。