為什么說++i的效率比i++高?
不知道你是否聽說過++i比i++快的說法,真的如此嗎?
++i與i++的區別
這兩個表達式從我們初學編程語言的時候就會接觸到。前者是自增后取值,后者是取值后自增。
我們看一個簡單的例子。
- #include <iostream>
- using namespace std;
- int main()
- {
- int a = 0;
- int b = 0;
- int c = a++;//int tmp = a;c=a;aa = a + 1
- int d = ++b;//bb = b + 1;d = b;
- cout<<"c="<<c<<";d="<<d<<endl;
- return 0;
- }
運行結果:
- c=0;d=1
對于這個結果我們并不感到意外。
另外我們還注意到另外一個有意思的現象:
- #include <iostream>
- using namespace std;
- int main()
- {
- int a = 0;
- int b = 0;
- int *c = &(a++);
- int *d = &(++b);
- return 0;
- }
編譯后報錯:
- main.cpp:7:19: error: lvalue required as unary ‘&’ operand
- int *c = &(a++);
說&作用于左值,也就是說a++的結果并非左值。但++b的結果是左值。
可簡單理解左值和右值:
- 左值,有名對象,可賦值
- 右值,臨時對象,不可被賦值
運算符重載
通過前面的例子也發現了,對于內置類型,前置自增返回對象的引用,而后置自增返回對象的原值(但非左值)。
基于上述原則,一個前置版本和后置版本的常見實現如下:
- class Test
- {
- public:
- Test& operator++();//前置自增
- const Test operator++(int);//后置自增
- private:
- int curPos; //當前位置
- };
- /*前置自增實現范式*/
- Test& Test::operator++()
- {
- ++curPos; //自增
- return *this; //取值
- }
- /*后置自增實現范式,為了與前置區分開,多了一個int參數,但從來沒用過*/
- const Test Test::operator++(int)
- {
- Test tmp = *this; //取值
- ++curPos; //自增
- return tmp;
- }
仔細觀察后,我們發現前置自增,先自增,后返回原對象的對象;沒有產生任何臨時對象;而后置自增,先保存原對象,然后自增,最后返回該原臨時對象,那么它就需要創建和銷毀,這樣一來,效率孰高孰低就很清楚了。
在不進行賦值的情況下,內置類型前置和后置自增的匯編都是一樣的呢!
- void test()
- {
- int i = 0;
- i++;
- //++i;
- }
匯編:
- push rbp
- mov rbp, rsp
- mov DWORD PTR [rbp-4], 0
- add DWORD PTR [rbp-4], 1
- nop
- pop rbp
- ret
不過,賦值的情況下,并且不開啟編譯器優化,它們的匯編代碼還是有差別的,有興趣的可以試試。
總結
對于內置類型,前置和后置自增或者自減在編譯器優化的情況下,兩者并無多大差別,而對于自定義類型,如無特別需要,人們似乎更加偏愛前置自增或自減,因為后置自增常常會產生臨時對象。
但是,又能提高多少效率呢?