在上次介紹顯示釋放資源的文章,我提到了C++/CLI中提供了一種新的對象執行個體化文法,就彷彿在native c++中在靜態棧上定義一個對象一樣。今天就讓我們來考察一下這個特性。另外,因為在上個文章裡回答Ninputer的一個問題時,犯了一個錯誤,我在這裡糾正一下...
定義以下以一個類。
ref class Test
{
Test(String^ s)
{
this->_s = s;
}
void Hello()
{
Console::WriteLine(_s);
}
property String^ Message
{
String^ get() { return _s;}
}
private:
String^ _s;
};
然後我們定義一個函數來使用這個類:
void foo()
{
Test t("Hello"); //沒有無參建構函式時的定義方法
t.Hello(); //使用“點”來訪問成員,而不是"->"符號
Console::WriteLine(t.Message); //訪問屬性
}
我們來看一下產生的IL代碼
.method assembly static void foo() cil managed
{
// Code size 29 (0x1d)
.maxstack 1
.locals init ([0] class Test V_0)
IL_0000: ldstr "Hello"
IL_0005: newobj instance void Test::.ctor(string)
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: call instance void Test::Hello()
IL_0011: ldloc.0
IL_0012: call instance string Test::get_Message()
IL_0017: call void [mscorlib]System.Console::WriteLine(string)
IL_001c: ret
} // end of method 'Global Functions'::foo
如果我們把上面的foo()函數改成gcnew的形式:
void foo()
{
Test^ t = gcnew Test("Hello");
t->Hello();
Console::WriteLine(t->Message);
}
然後觀察產生的IL代碼:
.method assembly static void foo() cil managed
{
// Code size 29 (0x1d)
.maxstack 1
.locals init ([0] class Test V_0)
IL_0000: ldstr "Hello"
IL_0005: newobj instance void Test::.ctor(string)
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: call instance void Test::Hello()
IL_0011: ldloc.0
IL_0012: call instance string Test::get_Message()
IL_0017: call void [mscorlib]System.Console::WriteLine(string)
IL_001c: ret
} // end of method 'Global Functions'::foo
可以看到產生的IL代碼與前面的是完全一致的。由此可見,這種新的文法只是編譯器提供的一種機制,並不是真的在Stack上執行個體化對象。那麼為什麼要提供這種文法呢?或者說兩種的區別在哪裡呢?那就是在上一篇文章中所提到的顯式釋放資源。只有用Stack方法來定義變數,才能獲得自動調用Dispose()方法的好處。
或許,我們今後應該盡量少用gcnew,除非你想手動控制Dispose()方法調用的時機。唯一比較遺憾的是,由於是從vc 2005 tool refreshes才開始支援這種文法,所以vc express的IDE中的intellisense功能還沒有跟上,使用“點”操作符時,沒有任何提示,不過相信在beta2裡一定會全面支援的。另外MS的員工還透露說beta2中,STL.NET可能就會現身了,真是非常期待。
另:
昨天Ninputer問我Stack定義的對象,是用->訪問成員還是用“點”來訪問成員。我回答是用->來訪問,其實應該是“點”才對。前幾天實驗的時候不知道為什麼編譯器沒有報錯......可惜代碼已經不在了,也沒法知道當時犯了什麼錯 今天寫這個帖的時候才發現不對了...