前言
注1:vc6、vs沒有提供編譯選項來關閉該優(yōu)化,無論是debug還是release都會進行RVO和復制省略優(yōu)化
注2:vc6、vs2005以下及vs2005+ Debug上不支持NRVO優(yōu)化,vs2005+ Release支持NRVO優(yōu)化
注3:g++支持這三種優(yōu)化,并且可通過編譯選項:-fno-elide-constructors來關閉優(yōu)化
RVO
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
|
#include <stdio.h> class A { public : A() { printf ( "%p construct\n" , this ); } A( const A& cp) { printf ( "%p copy construct\n" , this ); } ~A() { printf ( "%p destruct\n" , this ); } }; A GetA() { return A(); } int main() { { A a = GetA(); } return 0; } |
在g++和vc6、vs中,上述代碼僅僅只會調用一次構造函數和析構函數 ,輸出結果如下:
0x7ffe9d1edd0f construct
0x7ffe9d1edd0f destruct
在g++中,加上-fno-elide-constructors選項關閉優(yōu)化后,輸出結果如下:
0x7ffc46947d4f construct // 在函數GetA中,調用無參構造函數A()構造出一個臨時變量temp
0x7ffc46947d7f copy construct // 函數GetA return語句處,把臨時變量temp做為參數傳入并調用拷貝構造函數A(const A& cp)將返回值ret構造出來
0x7ffc46947d4f destruct // 函數GetA執(zhí)行完return語句后,臨時變量temp生命周期結束,調用其析構函數~A()
0x7ffc46947d7e copy construct // 函數GetA調用結束,返回上層main函數后,把返回值變量ret做為參數傳入并調用拷貝構造函數A(const A& cp)將變量A a構造出來
0x7ffc46947d7f destruct // A a = GetA()語句結束后,返回值ret生命周期結束,調用其析構函數~A()
0x7ffc46947d7e destruct // A a要離開作用域,生命周期結束,調用其析構函數~A()
注:臨時變量temp、返回值ret均為匿名變量
下面用c++代碼模擬一下其優(yōu)化行為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <new> A& GetA( void * p) { //由于p的內存是從外部傳入的,函數返回后仍然有效,因此返回值可為A& //vs中,以下代碼還可以寫成: // A& o = *((A*)p); // o.A::A(); // return o; return * new (p) A(); // placement new } int main() { { char buf[ sizeof (A)]; A& a = GetA(buf); a.~A(); } return 0; } |
NRVO
g++編譯器、vs2005+ Release(開啟/O2及以上優(yōu)化開關)
修改上述代碼,將GetA的實現修改成:
1
2
3
4
5
|
A GetA() { A o; return o; } |
在g++、vs2005+ Release中,上述代碼也僅僅只會調用一次構造函數和析構函數 ,輸出結果如下:
0x7ffe9d1edd0f construct
0x7ffe9d1edd0f destruct
g++加上-fno-elide-constructors選項關閉優(yōu)化后,和上述結果一樣
0x7ffc46947d4f construct
0x7ffc46947d7f copy construct
0x7ffc46947d4f destruct
0x7ffc46947d7e copy construct
0x7ffc46947d7f destruct
0x7ffc46947d7e destruct
但在vc6、vs2005以下、vs2005+ Debug中,沒有進行NRVO優(yōu)化,輸出結果為:
18fec4 construct // 在函數GetA中,調用無參構造函數A()構造出一個臨時變量o
18ff44 copy construct // 函數GetA return語句處,把臨時變量o做為參數傳入并調用拷貝構造函數A(const A& cp)將返回值ret構造出來
18fec4 destruct // 函數GetA執(zhí)行完return語句后,臨時變量o生命周期結束,調用其析構函數~A()
18ff44 destruct // A a要離開作用域,生命周期結束,調用其析構函數~A()
下面用c++代碼模擬一下vc6、vs2005以下、vs2005+ Debug上的行為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <new> A& GetA( void * p) { A o; //由于p的內存是從外部傳入的,函數返回后仍然有效,因此返回值可為A& //vs中,以下代碼還可以寫成: // A& t = *((A*)p); // t.A::A(o); // return t; return * new (p) A(o); // placement new } int main() { { char buf[ sizeof (A)]; A& a = GetA(buf); a.~A(); } return 0; } |
注:與g++、vs2005+ Release相比,vc6、vs2005以下、vs2005+ Debug只優(yōu)化掉了返回值到變量a的拷貝,命名局部變量o沒有被優(yōu)化掉,所以最后一共有2次構造和析構的調用
復制省略
典型情況是:調用構造函數進行值類型傳參
1
2
3
4
5
6
7
8
9
10
11
12
|
void Func(A a) { } int main() { { Func(A()); } return 0; } |
在g++和vc6、vs中,上述代碼僅僅只會調用一次構造函數和析構函數 ,輸出結果如下:
0x7ffeb5148d0f construct
0x7ffeb5148d0f destruct
在g++中,加上-fno-elide-constructors選項關閉優(yōu)化后,輸出結果如下:
0x7ffc53c141ef construct // 在main函數中,調用無參構造函數構造實參變量o
0x7ffc53c141ee copy construct // 調用Func函數后,將實參變量o做為參數傳入并調用拷貝構造函數A(const A& cp)將形參變量a構造出來
0x7ffc53c141ee destruct // 函數Func執(zhí)行完后,形參變量a生命周期結束,調用其析構函數~A()
0x7ffc53c141ef destruct // 返回main函數后,實參變量o要離開作用域,生命周期結束,調用其析構函數~A()
下面用c++代碼模擬一下其優(yōu)化行為:
1
2
3
4
5
6
7
8
9
10
11
12
|
void Func( const A& a) { } int main() { { Func(A()); } return 0; } |
優(yōu)化失效的情況
開啟g++優(yōu)化,得到以下各種失效情況的輸出結果:
(1)根據不同的條件分支,返回不同變量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
A GetA( bool bflag) { A a1, a2; if (bflag) return a1; return a2; } int main() { A a = GetA( true ); return 0; } |
0x7ffc3cca324f construct
0x7ffc3cca324e construct
0x7ffc3cca327f copy construct
0x7ffc3cca324e destruct
0x7ffc3cca324f destruct
0x7ffc3cca327f destruct
注1:2次缺省構造函數調用:用于構造a1、a2
注2:1次拷貝構造函數調用:用于拷貝構造返回值
注3:這兒仍然用右值引用優(yōu)化掉了一次拷貝函數調用:返回值賦值給a
(2)返回參數變量
(3)返回全局變量
(4)返回復合數據類型中的成員變量
(5)返回值賦值給已構造好的變量(此時會調用operator==賦值運算符)
以上就是詳解C++編譯器優(yōu)化技術的詳細內容,更多關于C++編譯器優(yōu)化技術的資料請關注服務器之家其它相關文章!
原文鏈接:https://www.cnblogs.com/kekec/p/11303391.html