C 物件誕生記之建構函式都幹了啥

2021-06-12 09:19:19 字數 3143 閱讀 5642

先看看msdn上對建構函式的描述:建構函式是一類特殊的方法,用於初始化型別和建立型別的例項。型別建構函式用於初始化型別中的靜態資料。型別建構函式由公共語言執行庫 (clr) 在建立型別的任何例項之前呼叫。型別建構函式是 static(在 visual basic 中為 shared)方法,不能帶任何引數。例項建構函式用於建立型別的例項。例項建構函式可以帶引數,也可以不帶引數。不帶任何引數的例項建構函式稱為預設建構函式。

先看乙個最簡單的:

public class program

// end main.

}// end class.

class classa

}

這裡什麼都沒做,只是生成了乙個classa例項,下面是il反彙編的建構函式的結果:
.method public hidebysig specialname rtspecialname 

instance void .ctor() cil managed

// end of method classa::.ctor

建構函式的反彙編結果名為 .ctor,可以看出雖然我們什麼都沒做,但是這個函式呼叫了system.object型別的建構函式。

看到這裡想到什麼了嗎?classa和object有神馬關係?沒錯,是繼承,classa預設繼承自object。好吧,再看乙個例子:

public class program

// end main.

}// end class.

class classb //隱式的繼承object

}class classa : classb //繼承classb

}

再看看我們的classa的 .ctor :

.method public hidebysig specialname rtspecialname 

instance void .ctor() cil managed

// end of method classa::.ctor

這次呼叫了classb的建構函式了,那麼classb呢 :

.method public hidebysig specialname rtspecialname 

instance void .ctor() cil managed

// end of method classb::.ctor

又是call指令,不解釋。

有興趣的可以試試更深的繼承樹,結果應該是一樣的

最後來乙個總結吧:c#建立例項的時候其建構函式會遞迴的呼叫父類的建構函式,直至繼承樹的根object

好吧,當前的討論範圍僅限例項建構函式,而c#中還有一種靜態建構函式,不我還是先繼續說說非靜態的吧。

現在改一改剛才的**:

public class program

// end main.

}// end class.

class classa

}

多了一行欄位的宣告及初始化,注意建構函式裡什麼都沒寫,現在看看a的建構函式的il**:
.method public hidebysig specialname rtspecialname 

instance void .ctor() cil managed

// end of method classa::.ctor

看到那個

ldc.i4 0xabc

了吧,再看看下一行

stfld int32 initializetest.classa::number

,前一行ldc是把乙個常數壓入棧,後一行stfld指令是從棧中獲取值替換例項成員,就是賦值(此處為初始化)。

看來欄位的初始化是建構函式的一部分,且最先執行,在遞迴呼叫父類建構函式之前

的把這個**再改一次:

public class program

// end main.

}// end class.

class classa

}

注意看il**的變化,和上面對比一下:

.method public hidebysig specialname rtspecialname 

instance void .ctor() cil managed

// end of method classa::.ctor

這次初始化的執行是在呼叫基類建構函式之後進行的,現在再加點語句:

class classa

}

il**:

.method public hidebysig specialname rtspecialname 

instance void .ctor() cil managed

// end of method classa::.ctor

大括號內的(我們真正寫在建構函式內的)**是放在最後執行的。

在編譯之後的.ctor方法中,我們使用c#編寫的語句,即那些可以"看得見的**"是放在最後執行的,在這之前是呼叫基類的構造方法,如果在宣告欄位的時候初始化了,那麼初始化的指令在遞迴地呼叫建構函式之前執行。

你可能感覺不是那麼直觀吧,最後這個**有興趣可以自己試一試,改一改:

public class program

// end main.

//只是為了賦值的時候能直**到.

public static int getval(int val)

", val);

return val;

}}// end class.

class classa

}class classb : classa

}class classc : classb

}

繼承樹是 a<-b<-c,字段初始化的順序是cba,而建構函式中的**的執行順序是abc.

《C程式設計伴侶》誕生記

引自圖靈教育陳冰老師的 編輯的話 我第一次看譚老師的 c程式設計 是20年前,那時我17歲,大學一年級。當時感覺這本書很神奇,對程式設計充滿了好奇。同時也感覺這本書有些難,有些地方,比如指標,雖然看過書,也有老師講過了,但依然感覺似懂非懂。還有些知識,書中只講了怎麼做,但沒有講為什麼要這麼做,比如為...

c 物件模型筆記之拷貝建構函式

拷貝建構函式 拷貝建構函式 以乙個物件的內容去初始化另個物件。關鍵在於初始化 有三種情況下會呼叫拷貝建構函式 class x 1 x x x xx x 不是賦值操作而是拷貝建構函式 2 void foo x x 物件引數 3 foobar 返回物件 如果類沒有提供顯示的拷貝建構函式,編譯器採用的是位...

C 之物件的引用與建構函式

定義 person 類,設定屬性 年齡。namespace 物件的引用 class person public person int age 帶引數的建構函式 定義 person 類,設定屬性 姓名,年齡。設定不帶引數的建構函式 設定帶名字的建構函式 設定帶年齡的建構函式 namespace 建構函...