一緒論
當微軟推出VSNET實現了可擴展的托管C++後C++程序員們反映不一盡管大部分的程序員對於能夠繼續使用C++感到很欣慰但幾乎所有的人對於托管C++提供的晦澀語法感到很痛苦微軟明顯從反饋中感覺到托管C++不是那麼成功
年月日ECMA(歐洲計算機制造商協會)宣布成立專家組負責結合ISO標准C++與通用語言開發一個可擴展語言的標准這個新的可擴展語言被稱為C++/CLI標准這個標准將被VSNET的C++編譯器支持
二老語法存在的問題
晦澀繁瑣的語法和文法這兩個雙重底線問題加重了閱讀的負擔
二流的CLI支持相對與C#與VBNETMC++使用不方便的工作區來提供CLI支持例如它沒有一個一一對應的結構來列舉NET的集合
C++與NET粗陋地結合對於CLI類型你不能使用C++的特色例如模板同樣對於C++類型你不能使用CLI的特色例如碎片帳集
令人混淆的指針非托管的C++的指針及托管的引用指針都使用*語法這非常令人混淆因為gc指針與托管指針在本質和行為上完全不同
MFC編譯器不能產生可校驗的代碼
三C++/CLI給我們提供了什麼?
優雅流暢的語法和文法C++/CLI為C++開發人員書寫托管代碼提供了一種非常自然的感覺並且它提供了非托管代碼到托管代碼的平滑過度以前所謂的雙重底線問題現在已經蕩然無存
一流的CLI支持CLI特色例如屬性碎片集合和屬類得到了直接支持此外C++/CLI還准許將這些特色用於本地非托管的類
一流的C++類支持C++特色例如模板和析構函數對於拖管和非拖管類繼續有效實際上C++/CLI是你可以表面上在棧或C++本地堆上聲明一個NET類型唯一的NET語言
在NET與C++之間的溝壑上架起了一座橋梁C++開發人員在抨擊BCL時不再象離開水的魚
C++/CLI編譯器產生的可執行文件完全是可校驗的
四Hello World小程序
using namespace System;
void _tmain()
{
Console::WriteLine(Hello World);
}
上述代碼除了不需要引用mscorlibdll庫外與老的語法沒有太大的區別因為無論你什麼時候使用/clr進行編輯編譯器都可以暗中進行引用(現在默認的是/clr:newSyntax)
五句柄
與老的語法主要的混淆是我們習慣於使用*符號來聲明拖管引用或非拖管指針在C++/CLI裡微軟引入了句柄的概念
void _tmain()
{
//The ^ punctuator represents a handle
String^ str = Hello World;
Console::WriteLine(str);
}
^符號代表一個托管對象(聲明時看上去象個帽子)按照CLI的規定句柄代表一個拖管對象的引用句柄在CLI中是新的語法相當於C++中的gc指針句柄與指針不再混淆在本質上兩者完全不同
六句柄與指針是怎樣區分開來的?
指針聲明時使用*符號而句柄使用^符號
句柄是針對拖管堆上對象的拖管引用而指針僅僅指向內存中的一個地址
指針很穩定GC循環不會影響到它句柄在基於GC或內存緊張的情況下可以指向不同的內存位置
對於指針程序開發人員必須顯式地刪除否則會面臨洩露的危險而對於句柄是否進行顯式刪除則完全根據程序人員的愛好了
句柄一定要指向一個具體的類型即所謂的類型安全性而指針明顯不是這樣你決不可以將一個句柄指向Void^類型
正如new操作符返回一個指針一樣gcnew返回一個句柄
七CLR對象示例
void _tmain()
{
String^ str = gcnew String(Hello World);
Object^ o = gcnew Object();
Console::WriteLine(str);
}
關鍵字gcnew用來實例化一個CLI對象而且它返回一個指向在CLR堆上的對象的句柄gcnew的優點在於它可以方便的讓我們區分拖管和非拖管的實例對象
大部分情況下gcnew關鍵字和^操作符提供了你用來進行BCL的一切手段但是很明顯你需要創建和聲明屬於自己的拖管類和接口
八聲明類型
CLR類型有一個形容詞前綴用來說明類型的種類下面是C++/CLI中的類型聲明示例
CLR types
o Reference types
§ ref class RefClass{};
§ ref struct RefClass{};
Value types
§ value class ValClass{};
§ value struct ValClass{};
o Interfaces
§ interface class IType{};
§ interface struct IType{};
o Enumerations
§ enum class Color{};
§ enum struct Color{};
Native types
o class Native{};
o struct Native{};
示例
using namespace System;
interface class IDog
{
void Bark();
};
ref class Dog : IDog
{
public:
void Bark()
{
Console::WriteLine(Bow wow wow);
}
};
void _tmain()
{
Dog^ d = gcnew Dog();
d>Bark();
}
上述程序中的代碼與老的C++語言相比看上去非常簡潔在以往的C++代碼中至少要用到gc和interface這兩個關鍵詞
九裝箱/拆箱操作
在C++/CLI中加箱是隱含的而且類型是安全的一個二進制的拷貝被執行並在CLR堆上形成一個對象去箱是顯式的僅僅需要使用reinterpret_cast操作符來解除引用
void _tmain()
{
int z = ;
Object^ o = z; //implicit boxing
int y = *reinterpret_cast<int^>(o); //unboxing
Console::WriteLine({} {} {}ozy);
z = ;
Console::WriteLine({} {} {}ozy);
}
// 輸出結果如下
//
//
在上述代碼中o對象是一個加箱的拷貝從第二個語句Console::WriteLine的輸出可以很明顯地看到它並沒有涉及到int類型的整數值
當你對一種數值類型進行加箱操作時返回的對象記住了最初的數值類型
void _tmain()
{
int z = ;
float f = ;
Object^ o = z;
Object^ o = f;
Console::WriteLine(o>GetType());
Console::WriteLine(o>GetType());
}
// Output
// SystemInt
// SystemSingle
因此不能對不同類型的對象進行去箱操作
void _tmain()
{
int z = ;
float f = ;
Object^ o = z;
Object^ o = f;
int y = *reinterpret_cast<int^>(o);//SystemInvalidCastException
float g = *reinterpret_cast<float^>(o);//SystemInvalidCastException
}
如果你非嘗試這麼做那麼你將得到一個SystemInvalidCastException讓我們來探討一下完美的類型安全性如果你要看內部代碼你將看到微軟的內部箱在實際中的運用例如
void Box()
{
float y=;
Object^ o = y;
}
編譯後的代碼是
maxstack
locals (float V_ object V_)
ldnull
stloc
ldcr
stloc
ldloc
box [mscorlib]SystemSingle
stloc
ret
根據微軟的內部文檔箱操作將未加工的類型轉換為一個具體類型的實例這項工作的完成通過創建一個新的對象並將數據拷貝到這個新分配的對象
十寫在後面的話
為什麼很多人已經可以使用CC++NET來開發程序但還在積極學習C++/CLI呢我想有四個方面的原因
從編譯器直到內層都還在支持C++代碼
C++/CLI對於其他標准來說無意是具有毀滅性地
與生俱來的內部支持勝過所有其他CLI語言
所有在MFC中出現的下劃線都已不再存在
From:http://tw.wingwit.com/Article/program/net/201311/11401.html