在以前的一篇文章裡我曾提到在C++/CLI中,會自動調用Dispose()方法。但那個時候只是從msdn上的文章中知道這樣一個特性,還沒有編譯器的支援,所以一切也只能是紙上談兵,很多細節都很不明朗。VC會以什麼樣的方式來提供這一特性,一直是我常思考的問題。然而甚至到VS.net 2005 beta1發布時,這一特性還沒有得到支援。
好在,在最新的Visual C++ 2005 Tool Refresh中的編譯器,已經支援了這一特性。那麼就讓我們來一探究竟吧。
首先,在最新的編譯器中,如果一個類定義了解構函式,編譯器會自動讓類實現IDisposable介面,並把解構函式映射到Dispose()方法上。假如我們定義以下這個類
它最終產生的IL代碼是這樣的。
我們可以看到它已經實現了IDisposable介面,並且Dispose方法的內容就是解構函式的內容。
其次為了能夠確定自動調用Dispose()方法的時機,C++/CLI現在支援一種新的對象執行個體化文法。
通常為了獲得一個類的執行個體,我們需要這樣寫:
Test^ t = gcnew Test();
但為了獲得自動調用Dispose()方法的好處,我們必須以這樣的文法來執行個體化類:
Test t;
這就彷彿native c++中在棧(Stack)上執行個體化了一個對象一樣。當然這隻是一個文法的模仿,真正的對象還是在堆中執行個體化的。然而以這種方法執行個體化,就告訴了編譯器,但退出對象申明所在的範圍時,希望能自動調用對象的Dispose()方法。
讓我們來看一個函數:
觀察函數的IL代碼,可以發現調用Dispose()方法的代碼已經被加入到方法中了。並且為了在拋出異常時,也能調用Dispose()方法,編譯器還自動加上了一個try塊。
以上的foo()函數,如果用C#來寫,會是這樣:
void foo()
{
using (Test t = new Test())
{
}
}
然而,大家都知道using有一個最大的缺點,就是一次只能執行個體化一個對象。而在ado.net的程式中,經常我們會需要一次申明幾個對象。這時候using語句就無能為力了。而C++/CLI提供的這一機制,完美地解決了這一問題。你可以一次申明多個對象,當這些對象退出範圍時,就會自動Dispose。
並且,範圍的識別方法和native c++中是一致的。也就是說你可以添加一對大括弧來構成一個範圍,而不一定要是一個函數,就像這樣:
值得注意的是,雖然在這個post中,我用了“對象退出範圍”這樣的詞,但是這隻是為了判斷Dispose方法調用的時機,並不表明對象在退出範圍之後就被回收了,對象記憶體的回收仍然是GC操控的,就像C#的using語句中那樣。
總得來說,C++/CLI在吸收了C#中using語句的經驗傳承之後,為顯式釋放資源提供了一個完美的解決方案。