C 解惑 2 物件的初始化順序

2022-01-30 23:16:17 字數 3295 閱讀 7143

我們先來看這樣一段**:

class foo

public void bar()

}class base

public base()

}class derived : base

public derived()

}static class program

}

猜一猜它的輸出結果是什麼?如果猜不出來,就執行一下看看吧。

derived static initializer

derived static constructor

derived initializer

base static initializer

base static constructor

base initializer

base constructor

derived constructor

是不是有點出乎你的意料?沒關係,我們來一步一步解釋。

上期已經介紹了建構函式的初始化順序,所以這次略過不談,直接來看看例項成員的初始化器。一般來說,我們在構造乙個型別的例項時,會先初始化成員,然後初始化建構函式(編譯器會把初始化成員的**編譯到建構函式**的最頂部)。但初始化乙個子類的時候,父類的成員、建構函式的初始化,和子類的成員、建構函式的初始化順序是什麼樣的呢?

我們把上面的**簡化一下,去掉靜態建構函式和靜態初始化器。

class base

}class derived : base

}

結果如下所示:

derived initializer

base initializer

base constructor

derived constructor

這可能會有點出乎你的意料,因為直觀上來說,似乎應該是先初始化父類的成員和建構函式,再初始化子類的成員和建構函式:

base initializers

base constructor

derived initializers

derived constructor

但實際上為什麼會先初始化子類的成員呢?這是因為,按照這樣的初始化順序,所有引用型別的唯讀字段(注意這裡的readonly並不是隨手寫寫的)都能確保在呼叫時不為null。而如果先初始化基類的成員和建構函式,就無法給出這樣的保證。

比如下面的**:

internal class base

public virtual void m()

}internal class derived : base

}internal class morederived : derived

}

如注釋所示,在構造derived型別的例項時,如果先初始化base的建構函式,後初始化derived的成員,那麼在base的建構函式中呼叫derivedn時,derivedfoo就會為null,因為它還沒有初始化。試想一下,你正在呼叫乙個物件的方法,但這個物件的字段沒有初始化,建構函式也還沒有執行,這顯然是不合理的。

同樣,在構造morederived時,在base的建構函式中呼叫m(進而呼叫n)也會得到空引用,因為derivedderivedfoo仍然沒有初始化。

注意儘管類似if (this is derived) (this as derived).n();這樣的**是合法的,但是一定注意不要這樣寫。在基類的建構函式中,把「自己」轉換為自己的子類,想想都不可思議……

因此,型別的初始化順序必須是這樣的:

derived initializer

base initializer

base constructor

derived constructor

我們都知道,靜態建構函式是乙個特殊的建構函式,它在該型別的所有成員(包括例項建構函式)第一次被訪問之前執行。而與例項的初始化器會在例項建構函式之前執行類似,靜態初始化器會在靜態建構函式之前執行。結合這兩點,我們來看看本文最初的謎題。在執行new derived()時,是第一次訪問derived類,此時會率先執行它的靜態建構函式,而在執行靜態建構函式之前,會執行靜態初始化器。因此列印的結果應該為:

derived static initializer

...derived static constructor

...derived constructor

現在問題來了,基類的靜態建構函式會被執行嗎?如果會,是在什麼時候執行的呢?會和例項建構函式一樣,在子類的靜態初始化器之後嗎?

derived static initializer

base static initializer

base static constructor

derived static constructor

稍加思考我們就能得出答案。由於靜態初始化器和靜態建構函式都是靜態的,所以在執行的時候並不會出發基類的任何行為(記住我們前面說的,只有當類的成員被呼叫的時候,才會執行靜態初始化器和靜態建構函式)。因此在它們之後應該繼續執行子類的例項初始化器。而在這之後,按順序該執行基類的例項初始化器了,這時基類的成員第一次被呼叫,會出發基類的靜態初始化器和靜態建構函式,此後再執行基類的例項初始化器,並按順序繼續執行下去。

因此最終的結果為:

derived static initializer

derived static constructor

derived initializer

base static initializer

base static constructor

base initializer

base constructor

derived constructor

8 物件初始化

1.分配物件 分配 allcoation 是乙個新物件誕生的過程。向某個類傳送alloc訊息,就能為類分配一塊足夠大的記憶體,以存放該類的全部例項變數。同時alloc方法還順便將這塊記憶體區域全部初始化為0。objective c將分配和初始化拆分為兩個明確的步驟 來自nsobject的類方法all...

My Java之筆記(3) 物件初始化順序

class bird public bird class raptor extends bird public raptor static class hawk extends raptor public hawk public static void main string args 執行結果 祖...

20140111 物件初始化過程

c 物件初始化 1.先變數後建構函式。變數先被初始化,然後建構函式被執行 2.先靜態化後例項化。當乙個類被訪問時,靜態變數和建構函式最先被初始化.接著是物件的例項化變數和建構函式被初始化 3.先派生類後基類。對於變數和靜態建構函式,派生物件在基物件之前被初始化.比如c類派生自b類,b類派生自a類,那...