建構函式語義學之程式轉化語義學 2

2021-06-21 10:45:20 字數 2852 閱讀 3370

在 建構函式語義學之程式轉化語義學(1) 中編譯器做了一些優化,有時他還會給你的程式更多的優化:

(1) 在使用者層面做優化

如果程式設計師頂乙個計算用的 constructor:

x bar( const t &y, const t &z )

x xx;

// ...以 y 和 z 來處理 xx

return xx;

有的編譯器開發人員會另外定義乙個 constructor:

x bar( const t &y, const t&z )

return x( y,z );

定義被轉化之後,再做如下的優化:

void ( x &__result , const t &y, const t &z) //我覺得侯捷大師說得對哈

__result.x::x( y, z );

return;

這樣就不用__result就可以被直接計算出來而不用經由 copy constructor 拷貝而得。不過這種解決方法也受到了某種批評,怕那些特殊用途的 constructor 可能會大量擴散。在這個層面上,class 的設計是以效率考慮居多,而不是以「支援抽象化」為優先!

(2) 在編譯器層面做優化

x bar()

x xx;

// ...處理 xx

return xx;

編譯器把其中的 xx 用 __result取代

void bar( x &__result)

// default constructor 被呼叫

// c++偽碼

__result.x::x();

// 直接處理 __result;

return ;

這樣的編譯器優化操作,有時候被稱為 named return value 優化!nrv優化如今已被思維標準 c++ 編譯器的乙個義不容辭的優化操作,但 nrv 優化在每個編譯器上的規則可能不一樣。

為什麼以上1,2被分為 在使用者層面( user-level ) 和 在編譯器層面( compiler-level )做優化呢?大家看看哈,在(1)中是在使用者**中另加了乙個 constructor ,故屬於使用者層面。而 (2) 呢,是在將使用者的**在編譯的時候進行優化,故被稱為編譯器層面優化!

class a                        class a

public:                         public:

a()                            a():name(0),num(0)

name = 0;                          

num = 0;                         num = 0;

private:                         private:      

string name;                      string name;

int num;                         int num;

請大家對比一下上面 class a 的建構函式的不同,並說出哪種**在執行時會更快?

答案當然是第二種,我相信很多人都知道這一結果,那這是為什麼呢?這要從編譯器的優化說起了

上面的第一種寫法會被編譯器轉化成如下**:

a::a()

// 呼叫 string 的 default constructor

name.string::string();

//產生暫時性物件

string temp = string( 0 );

// "memberwise" 地拷貝 name

name.string::operator = ( temp );

temp.string::~string();

num = 0;

而第二種寫法會被編譯器轉化成如下**:

a::a()

//呼叫 string ( int )constructor

name.string::string( 0 );

num = 0;

從轉化後的**可看出成員初始化列表的方式明顯快的多,所以大家以後遇到以上情況時,盡量寫成成員初始化列表的形式以使你的程式跑的更快。不過一下四種情況可由不得你了,你必須用成員初始化列表的形式也就是上面第二種寫法,而不能採用第一種寫法。

(1). 當初始化乙個 reference member 時。

(2). 當初始化乙個 const member 時;

(3). 當呼叫乙個 base class 的constructor ,而它擁有一組引數時。

(4). 但呼叫乙個 member class 的 constructor,而它擁有一組引數時。

繼續往下看如下**

class x 

int i;

int j;

public:

x(int val):j(val),i(j)

咋看上去上面的初始化**沒錯,先把 val 值賦給 j,再將 j 的值賦給 i。但實際上是這樣嗎?來看看編譯轉化後的**:

x::x(int val)

// 呼叫 int 的 constructor 來初始化 i 和 j

// 偽c++**

i = j;

j = val;

許多人看完這個轉化後的嗎之後肯定會一目了然,知道上面**是有問題的了,並沒有達到先賦值給 j 然後再賦值給 i 的目的!讀者肯定要問為什麼 i = j 會在前面呢?那是因為 class x 在定義 date member 時先定義 i,編譯器在優化時會在 constructor 中將先宣告的 data member 先初始化。故我們那些寫是不對的啦!這樣 i 會是乙個不確定的值。所以下次,該怎麼寫你的**了吧?^_^

建構函式語義學

有些書上說,如果乙個類中沒有任何的建構函式,那麼編譯器會為我們預設的合成乙個 合成預設規則函式 其實,系統是在 必要的時候 才會為我們合成預設的建構函式。這個可以去分析obj檔案 情況1 如果乙個類中沒有任何的建構函式,且它的成員變數中含有乙個類型別的成員,那麼這個時候系統會為這個類合成乙個預設的建...

C 函式語義學

我們或許會認為呼叫類成員函式的開銷會大於呼叫普通函式,但是其實不是這樣的,呼叫普通成員函式和全域性函式開銷差不多,我們可以在vs中除錯,檢視反彙編 普通成員函式在呼叫的時候編譯器會在傳遞乙個物件的this指標 eg includeusing namespace std class a void te...

C 虛函式語義學

c 中的多型通過虛函式表實現,需要在執行時,根據指標所指物件的實際型別,根據物件中的虛函式表指標來索引相應的虛函式來呼叫。對於class derived public base1,public base2 base2 pbase2 new derived pbase2會調整this指標,使pbase...