C++/CLI

C++/CLI（CLI: Common Language Infrastructure）在計算機語言中是一門由微軟設計，用來代替 C++ 託管擴充套件（Managed C++，下文使用 MC++ 指代）的語言。這門語言在相容原有的 C++ 標準的同時，重新簡化了託管程式碼擴充套件的語法，提供了更好的程式碼可讀性。和微軟.NET 的其他語言一樣，微軟向 ECMA 提交了 C++/CLI 的標準化請求，並且被 ECMA 透過成為正式的標準。C++/CLI 現在可以被 Visual C++ 2005 和更高版本的編譯器支援。C++/CLI 的部分特性已經申請了專利。

語法改變

C++/CLI 是一門獨立的語言（比如新的關鍵字），而不是像 C++ 託管擴充套件一樣是 C++ 的超集。（C++ 託管擴充套件有一些不標準的關鍵字如gc 和value）。所以，C++/CLI 對於這些語法有較大的改變，尤其是去除了一些意義不明確的關鍵字，增加了一些對.NET 的特性的語言級別的支援。

1.1 關鍵字

有歧義的語法，像 MC++ 的不同版本用新的運算子 gcnew 區分：在 C++/CLI，.NET 引用型別的建立需要要使用新的關鍵字 gcnew，以和本地型別區分開。

C++/CLI 增加了新的泛型概念（書寫方式與 C++ 的模板相似，但行為有很大的測試區別）。

資料型別

2.1 基本資料型別

2.2 控制代碼（Handle）代替了指標

回到 MC++，有兩類指標：用nogc 標識的指標是傳統意義上的 C++ 指標，而用gc 標識的指標為.NET 中的引用。但在 C++/CLI 裡，唯一的指標就是傳統意義上的 C++ 指標，而.NET 引用型別使用一個 “控制代碼” 來獲取，使用新的語法 “類名 ^” 代替了 MC++ 的 “類名 *”。新的句法使得託管和非託管程式碼混合開發更加方便；它指明瞭物件將會被垃圾回收器自動銷燬還是手動銷燬。

範例程式碼：

// C++ 託管擴充套件

using

using namespace System::Collections;

gc class referencetype

{

protected:

String* stringVar;

int intArr gc;

ArrayList* doubleList;

public:

referencetype (String str，int pointer，int number) // 哪個是託管的？{doubleList = new ArrayList ;

System::Console::WriteLine (str->Trim + number.ToString );

}

};

// C++/CLI

using

using namespace System::Collections::Generic;

ref class referencetype

{

protected:

String^ stringVar;

array intArr;

List^ doubleList;

public:

referencetype (String^ str，int* pointer，int number) // 不會再分不清了吧？{doubleList = gcnew List ;

System::Console::WriteLine (str->Trim + number);

}

};

2.3 託管型別的定義

在 CLR 中，託管型別是分為引用型別（class）和值型別（struct）的，在 C++/CLI 中的分別定義方式如下：

引用型別：

public ref class MyClass

{

};

值型別：

public value class MyClass

{

};

2.4 陣列

陣列現在需要用 cli 名字空間內的 array 類宣告，語法和 STL 的 vector 類似。

C++/CLI 中新增了 array ^ 的方式定義陣列。

array ^a = gcnew array (100) {1, 2, 3};

或者使用它的完整版：

cli::array ^a = gcnew cli::array {1, 2, 3};

2.5 System::String

include

using namespace std;

using namespace System;

using namespace System::Runtime::InteropServices;

string casttostring (String^ str)

{IntPtr ip = Marshal::StringToHGlobalAnsi (str);

const char* ch = static_cast (ip.ToPointer );

string stdStr = ch;

Marshal::FreeHGlobal (ip);

return stdStr;

}

2.6 不定引數

對於 C# 中的不定引數的語法：

void foo (params string args)

在 C++/CLI 中對應的版本為：

void foo (... array^ args)

2.7 跟蹤引用（Tracking reference）

C++/CLI 裡的一個 “跟蹤引用” 也是一個控制程式碼，但它是傳地址而不是傳值。等同於在 C# 中加了 “ref” 關鍵字，或 Visual Basic .NET 的 “ByRef”。C++/CLI 使用 “^%” 語法來定義一個跟蹤引用。與傳統 C++ 中的 “*&” 語法相似。

下面的示例了 “跟蹤引用” 的使用。如果把 “^%” 改成 “^”（也就是使用普通的控制代碼），10 個字串將不會被修改，而只會生成那些字串的副本，這些都是因為那些引用已經不是傳地址而是傳值。

int main

{array^ arr = gcnew array (10);

int i = 0;

for each (String^% s in arr)

s = gcnew String (i++.ToString );

return 0;

}

上面的程式碼示例了使用者如何用 C++/CLI 做一些其他.NET 語言不能做的事情，比如 C# 就不允許在 foreach 迴圈中這樣做。例如 foreach (ref string s in arr) 在 C# 中是非法的。

2.8 析構 (Finalizer/Destructor)

C++/CLI 的另一個變化就是使用 “! 類名” 來宣告一個託管型別的 “析構方法”（在垃圾回收器回收物件之前的不確定的時間由 CLR 呼叫），而原來的 “~ 類名” 是用來定義 “傳統的解構函式”（能被使用者自己呼叫）。另外，下面的例子說明了如何在 C++/CLI 中託管物件如何自動呼叫 “傳統解構函式”。

在一個典型的.NET 程式中（例如直接使用 CLI）程式設計，可以由使用者自己呼叫的 “解構方法” 是用實現 IDisposable 介面，透過編寫 Dispose 方法來實現顯式釋放資源；而不確定的 “解構方法” 是透過過載 Finalize 函式來實現的。

// C++/CLI

ref class MyClass // :IDisposable (編譯器自動實現 IDisposable 介面)

{

public:

MyClass ; // 建構函式

~MyClass ; // (確定的) 解構函式 (編譯器使用 IDisposable.Dispose 來實現)

protected:

MyClass ; // 解構方法 (不確定的) (編譯器透過過載 virtual void Finalize 來實現)

public:

static void Test

{

MyClass _auto; // 這不是個控制程式碼，它將呼叫 MyClass 的預設建構函式

// 使用_auto 物件

// 函式返回前自動呼叫_auto 的解構函式 (IDisposable.Dispose，由～MyClass 定義) 來釋放資源

// 以上程式碼等效於:

MyClass^ user = gcnew MyClass ;

try {/ 使用_auto 物件 /}

finally {delete user; / 由編譯器呼叫_auto.Dispose / }

}

};

// C#

class MyClass : IDisposable

{public MyClass // 建構函式

~MyClass // 析構方法 (不確定的) (編譯器透過過載 virtual void Finalize 來實現)，與 C++/CLI 的！MyClass 等效

public void Dispose // Dispose 方法

public static void Test

{using (MyClass auto = new MyClass )

{/ 使用 auto 物件 /}

// 因為使用了 using 句法，編譯器自動呼叫 auto.Dispose

// 以上程式碼等效於:

MyClass user = new MyClass ;

try {/ 使用 user 物件 /}

finally {user.Dispose ; }

}

}