C++/CLI 概觀 王建興 qing@cs.nthu.edu.tw (Email/MSN) 2007/1/25
About Qing Education Ph.D. Candidate, Department of Computer Science, National Tsing-Hua University, Taiwan Research interests: distribute network management, mobile agent, VoIP, and p2p networking Software Development Skills Programming languages: 80x86 assembly, C/C++, Java, C# J2EE development and Web programming: EJB, JSP/Servlet Network programming: TCP/IP, socket programming Object Oriented Design/Programming Design Patterns and Software Architecture  Distributed Network Management System Peer-to-Peer Networking Book Translation Thinking in Java 2nd Edition, in Traditional Chinese Essential C++, in Traditional Chinese
Agenda 何謂 C++/CLI C++/CLI 中的型別宣告 Handle vs. Pointer Deterministic Destruction  of C++/CLIS C++/CLI 中的陣列 C++/CLI 對 Property 的支援 C++/CLI 的四種編譯模式 Templates 及 Generics C++/CLI 的標準化
在 C++/CLI 出現前, .NET 上的 C++ 程式員恐怕只有一個字可以形容 悶
.NET 上的 C++ 程式員為什麼悶? (1/2) 僅管有了 Managed Extension for C++ ,但卻存在一些對程式員的困擾 不夠流暢優雅的語法 例如:雙底線的關鍵字們 對 CLI 的支援不夠支接 例如:缺乏 for-each 語法,不支援 property C++ 與 .NET 之間的整合不夠好 例如:無法在 CLI 型別上使用 template ,也無法在 C++ 型別上運用 CLI 的機制,例如垃圾回收 *http://www.codeproject.com/managedcpp/cppcliintro01.asp
.NET 上的 C++ 程式員為什麼悶? (2/2) 有兩種形式的指標: unmanaged  指標及 managed 指標,其用法及表示容易造成混淆 無法產生 verifiable 的程式碼
為什麼不學 C# 就好了? 多學一個程式語言 C++ 最佳化能力較強 C++ 具備 deterministic destruction (即使編譯為 MSIL ) C++ 程式員在 C++ 的特性支持下,生產力較高 STL template specialization
好消息是… .Net 上的 C++ 程式員不用再悶了
C++/CLI 所帶來的拯救 優雅流暢的語法 對 CLI 的直接支援 property ,泛型,垃圾回收等機制全都有了直接的支援 在 unmanaged classes 上這些機制也都適用 妥善的橋接 .NET 與 C++ 透過 C++/CLI 編譯出來的程式碼,是 fully verifiable *http://www.codeproject.com/managedcpp/cppcliintro01.asp
何謂 C++/CLI ? C++ 所支援的靜態物件模型,目標放在對執行檔速度及大小的最佳化上 CLI 則為一支援動態元件編程模型的多階架構 C++/CLI 中的” /” ,代表 C++ 與 CLI 之間的 binding 何謂 C++/CLI 近似說法 1 : C++/CLI 將 C++ 靜態的物件模型繫結 (bind) 至 CLI 的動態元件模型 近似說法 2 : C++/CLI 將 .NET 編程模型整合進到 C++ 中 * Stanley B. Lippman , http://msdn.microsoft.com/msdnmag/issues/06/00/PureC/default.aspx
C++/CLI 就是 C++ X CLI 確定性的記憶體管理 Template Native types STL 、 generic algorithms 指標 複製建構及指派 垃圾收集, finalizer 泛型 Reference 及 value types Interface Verifiability Security Properties, delegates, events 功能強力的 BCL C++ 的特點 CLI 的特點 C++/CLI 發揮 C++ 及 CLI 的相乘效果
C++/CLI 的目標就是 無接縫式的整合 Unmanaged 及 Managed Code
C++ 及 CLI 物件模型的比較 物件儲存空間類型多樣化 static, stack,  以及 heap 物件的生命期類型多樣化 static object 有著和程式相同的生命期 stack 上的 object ,則在和 scope({ … }) 的生命期相同 heap 上的 object ,則由程式員自行操控其生命期 物件建構及指派的深層拷貝模型 編譯時,原始碼中必須含括所有動用型別的資訊 物件儲存空間類型單純 Value type 儲存於 stack Reference type 儲存於 managed heap 上 物件的生命期具不確定性 Reference types 須倚靠垃圾收集器回收 物件的指派採淺層參考語義 編譯時,型別資訊由所使用型別的 metadata 提供 C++ 的靜態物件模型 CLI 的動態物件模型
C++/CLI 中的型別宣告 在 C++/CLI 中可宣告下述型別 CLR 的 enumeration type enum class E {…} CLR 的 interface type interface class I {…} CLR 的 value type value class V {…} CLR 的 reference type ref class R {…} C++ 原生類別(過去 C++ 程式員所用) class N {…} 所宣告的型別 宣告語法
C++ 與 CTS 型別的對應 N/A Boolean bool N/A Double long double N/A Double double N/A Single float UInt64 Int64 __int64 UInt32 Int32 long int UInt32 Int32 int, __int32 UInt16 Int16 short int Byte Sbyte char CTS Unsigned Type CTS Signed Type C++ Type
各型別的用途 原生型別 具備 native code 的語義及優點 即使被編譯成 MSIL 亦如此 Reference Type 可被垃圾收集器回收,提供簡便的記憶體管理模式 Value Type 輕量級的型別(例如像整數之類的型別) Interface Type 宣告 CLR 中的介面型別 Enumeration Type 宣告 CLR 中的列舉型別
例:在 C++/CLI 中宣告 Reference Type ref  class Qing { public: void sayHello(); }; void Qing::sayHello() { Console::WriteLine("Hello!"); } 多半宣告在 .h 檔裡 多半宣告在 .pp 檔裡
Handle vs. Pointer C++/CLI 中將 native C++ 型別及 CLI 型別區分開來 C++/CLI 中同樣具備指向 CLI 型別的 ” 指標”,但採用了不同的符號來表示,同時賦予一個不同的名稱,以資區別 這個名稱就是 Handle (相對於 Pointer ) 這個符號就是 ^ (相對於 * ) 建構 native C++ 型別及 CLI 型別的關鍵字也已有所區隔 new :產生 native C++ 型別 gcnew :產生 CLI 型別( gc 意指 garbage collected ,垃圾回收之意)
透過 Handle 建構並運用 CLI 型別 Qing  ^ qing =  gcnew  Qing(); qing->sayHello();
使用 Pointer 和 Handle 的差異 Pointer 用來在 native heap 中配置空間 語法: T* t = new T; 指標指向的位置是穩定的(不受 GC 影響),甚至可以被轉型為 int 倘若未自行釋放記憶體( delete ),則會產生 memory leak Handle 用來在 managed heap 中配置空間 語法: T^ t = gcnew T;  GC 會自動釋放位於 managed heap 中的物件所佔用的空間 但程式員仍可針對 managed heap 上的物件呼叫 delete
不同的兩種物件生成及運用模式 *Kate Gregory, “Moving C++ Applications to the Common Language Runtime”     Use Native Heap    Use Managed Heap   Use Stack ^ and % always * and & never Verifiability d elete delete Free gcnew new Allocate % & Reference ^ * Pointer / Handle Managed Native
於 Native/CLI 型別中混用 Pointer/Handle (1/2) 在 CLI 型別中使用 Handle 在 CLI 型別中使用 Pointer ref class R { private: String ^str; } ref class R { private: std::string* str;  }
於 Native/CLI 型別中混用 Pointer/Handle (2/2) 在 Native 型別中使用 Handle gcroot<T> 是用來包裝 System.Runtime.InteropServices.GCHandle 的一個 template class *http://www.codeproject.com/managedcpp/ijw_unmanaged.asp class N { private: gcroot<String^> str;; }
Tracking Reference Operator % & 之於 pointer ,相當於 % 之於 handle R1 ^pr1 = gcnew R1(); R1 %r1 =  * pr1; Console::WriteLine(r1 . ToString()); 不再是  -> 同樣使用 *
Tracking Reference 的效應 array<String^>^ arr = gcnew array<String^>(3); int i = 0; for each(String^% s in arr) s = gcnew String(i++.ToString()); for each(String^ s in arr) Console::WriteLine(s); 執行結果: 0 1 2 array<String^>^ arr = gcnew array<String^>(3); int i = 0; for each(String^ s in arr) s = gcnew String(i++.ToString()); for each(String^ s in arr) Console::WriteLine(s); 執行結果:
CLI 的 Non-Deterministic Finalization CLI 的垃圾回收機制,會在記憶體傾向不足時,將不再使用被物件佔用的記憶體空間回收再用 當物件佔用的記憶體空間確定被回收前的一刻,該物件的 Finalize() 會被呼叫,此即為 finalization (終始化)動作 Finalization 執行的時機及是否會被執行任誰都說不得準 所以被稱為 non-deterministic finalization
Disposal 對於記憶體資源而言,  non-deterministic finalization 不成問題 但對非記憶體資料(例如資料庫 、 檔案), 面對 non-deterministic finalization 的程式員得多費心才能使程式如預期的運作 對 .NET 而言,慣例上清理此類資料的動作,會定義於名為 Close() 或 Dispose() 的方法中 但這麼一來,程式員得自行呼叫 Close() 或 Dispose() 來進行清理動作
Deterministic Destruction C++/CLI 除了 CLI 上原先就有的 finalizer 之外,還提供了 destructor. ref class R1 { public: R1() {} ~R1() {} protected: !R1() {}  }; constructor destructor finalizer * 注意: C++/CLI 使用了 C# 中用來表示 Finalizer 的符號來表示 Destructor
Finalizer vs. Destructor C++/CLI 中的 Destructor 最終會被編譯成為 Dispose() ,如果用 C# 的相對應程式碼來看的話,會像是: GC::SuppressFinalize() 會要求系統不要呼叫物件的 finalizer() ,避免物件被清理兩次 public void Dispose()//IDisposable::Dispose { GC.SuppressFinalize(this); } *http://www.codeproject.com/managedcpp/cppclidtors.asp *http://msdn2.microsoft.com/en-us/library/system.gc.suppressfinalize.aspx
Why  SuppressFinalize?   public class FileStream : Stream { public override void Close() { // Clean up this object: flush data and close file  … // There is no reason to Finalize this object now GC.SuppressFinalize(this); } protected override void Finalize() { Close();  // Clean up this object: flush data and close file } // Rest of FileStream methods go here … } *http://www.codeproject.com/managedcpp/cppclidtors.asp 如果程式員自行呼叫了 Close() , 但 GC 又呼叫了 Finalize() 便會引發 Close() 被叫用兩次
自動進行的 Disposal { SqlConnection conn(connString); } constructor 會被呼叫 destructor 會被自動呼叫 {   SqlConnection ^pConn = gcnew SqlConnection(connString); } constructor 會被呼叫 之後就要等待 GC 動作了
C++/CLI 中的陣列 使用 Managed Heap 的 Managed Array ,其基礎型別皆為 System::Array 使用 C++/CLI 的陣列時,可以想像存在一個虛擬的 template http://www.codeproject.com/managedcpp/cppcliarrays.asp namespace stdcli::language { template<typename T, int rank = 1> ref class array : System::Array {}; }
陣列定義語法 一維陣列語法 多維度陣列語法 array<String ^> ^ strArray = gcnew array<String ^>(10); for(int i=0;i<strArray->Length;i++) { strArray[i] = &quot;&quot;+i; Console::WriteLine(strArray[i]); } array<String ^, 3> ^ strArray = gcnew array<String ^, 3>(4, 3, 2); 維度 每個維度的長度
陣列的初始化 array<String^>^ strarr = gcnew array<String^> {“String1&quot;, “String2&quot;}; array<String^>^ strarr2 = {“String1&quot;, “String2&quot;}; array<Object^,2> ^ objarr = {{“String1&quot;, 1}, {“String2&quot;, 2}};
參數陣列 C++/CLI 支援參數陣列,參數陣列必須是最後一個參數 用…來標示參數陣列 void testParamArray(String ^s,  ...  array<String ^>^ params) { Console::WriteLine(s+&quot;: &quot;); for(int i=0;i<params->Length;i++) Console::WriteLine(params[i]); } int main(array<System::String ^> ^args) { testParamArray(&quot;Hello&quot;, &quot;qing&quot;); testParamArray(&quot;Hello&quot;, &quot;qing&quot;, &quot;chrisma&quot;); }
Property 的語法 ref class UserAccount { public: property String^ ID { String^ get(){return id;} virtual void set(String^ value){id = value;} } private: String^ id; }; 就和 C# 的定義方式類似
Index Property 的語法 ref class OnlineUserList{ public: property UserAccount^  User[int]  { UserAccount^ get(int idx){return (UserAccount^) alUser[idx];} } OnlineUserList() { alUser = gcnew System::Collections::ArrayList(); } // …  private: System::Collections::ArrayList ^alUser; };
Refernece Type 允許單一繼承多重實作 ref class R abstract {};  public ref class R2 : R, IClone, IComparable, IDisposable, IEnumerable { };  所有 reference type 都繼承自 System::Object 最多繼承一個類別,但可以實作多個介面
Exception Handling try { throw gcnew Exception(&quot;qing&quot;); } catch(System::Exception^ e) { Console::WriteLine(e->StackTrace); }
CLR 編譯模式:共有四種可供選擇 Mix( /clr) 包含 managed/unmanaged code Pure( /clr:pure ) 僅包含 managed code 但仍可使用 #include 並呼叫原生 API Verifiable( clr:safe) 僅包含 verifiable code Managed Extensions for C++( clr:oldSyntax ) 用來編譯舊式的程式碼
在 VS 2005 中選擇編譯模式
編譯模式之間的關連性 Native CLR Code Data Machine Code CLR Data / Types Native Data / Types MSIL Code Mixed C++ /clr Native C++ Verifiable C++ /clr:safe Pure C++ /clr:pure
CLR 編譯模式帶來的應用 在 C++ 應用程式中直接使用 BCL 或以 .NET 寫成的 library 大幅提高生產力 將 native code 的功能,以 .NET 物件模型的方式對外提供 便於其他 .NET 應用程式使用( ASP.NET )
決定 Managed/Unmanaged 的交界處 C++ managed C++ CRT, STL, etc One call to foo() Hundreds of calls C# or C++ managed C++ CRT, STL, etc One call to foo() Hundreds of calls C# C++ CRT, STL, etc Hundreds of calls C++ One call to foo() One call * Kate Gregory, “ Moving C++ Applications to the Common Language Runtime”
C++/CLI 支援 Template template<typename T1, typename T2> class NativeData { public: NativeData(T1 t1) { m_t1 = t1; } void DoStuff(T2 t2) { //... } private: T1 m_t1; }; *http://www.codeproject.com/managedcpp/cppcligenerics.asp
Tempalte 採用 Lazy Constraint (1/2) template<typename T> class Native { public: void Start(int x) { T* t = new T(); t->Bark(x); t->WagTail(); delete t; } }; 如何確定 t 有 Bark() 及 WagTail() 兩 methods 呢? *http://www.codeproject.com/managedcpp/cppcligenerics.asp
Tempalte 採用 Lazy Constraint (2/2) Native<NativeDog> d1; d1.Start(100); Native<NativePig> d2; d2.Start(100); 引發編譯器錯誤: error C2039: 'Bark' : is not a member of 'NativePig' *http://www.codeproject.com/managedcpp/cppcligenerics.asp
C++/CLI 支援 Generics generic<typename T1, typename T2> ref class GenericData { public: GenericData(T1 t1) { m_t1 = t1; } void DoStuff(T2 t2) { //... } private: T1 m_t1; }; *http://www.codeproject.com/managedcpp/cppcligenerics.asp
Generics 採用 Subtype Constraints (1/3) generic<typename T>  where T:IDog  ref class GenRef  { public: void Start(int x) { T t = Activator::CreateInstance<T>(); t->Bark(x); t->WagTail(); delete safe_cast<Object^>(t);  } }; 透過限制 T 必須實作的介面來進行約束 *http://www.codeproject.com/managedcpp/cppcligenerics.asp
Generics 採用 Subtype Constraints (2/3) ref class ClrDog : IDog { public: virtual void Bark(int Loudness) { Console::WriteLine(&quot;ClrDog::Bark {0}&quot;,Loudness); } virtual void WagTail() { Console::WriteLine(&quot;ClrDog::WagTail&quot;); } }; *http://www.codeproject.com/managedcpp/cppcligenerics.asp
Generics 採用 Subtype Constraints (3/3) GenRef<ClrDog^> g1; g1.Start(100); *http://www.codeproject.com/managedcpp/cppcligenerics.asp
使用 Reference Types 於 Template template<typename T> class CLR { public: void Start(int x) { T^ t = gcnew T(); t->Bark(x); t->WagTail(); delete t; } }; CLR<ClrDog> g2; g2.Start(100)
將 Template 宣告為 Reference Type template<typename T>  ref  class CLR2 { public: void Start(int x) { T^ t = gcnew T(); t->Bark(x); t->WagTail(); delete t; } }; CLR 2 <ClrDog> g 3 ; g 3 .Start(100) Console::WriteLine(g3.GetType()->Name); 執行結果: ClrDog::Bark 100 ClrDog::WagTail CLR2<ClrDog>
Generics Functions generic<typename T> where T:IDog void DoAll(T t) { t->Bark(0); t->WagTail(); } *http://www.codeproject.com/managedcpp/cppcligenerics.asp
Template vs. Generics Templates are instantiated at  compile-time  with the source code.  The type checking of a template are performed at the point where the template is   defined  and  instantiated . Tempaltes  allow  specialization. Templates  allow  non-type parameters.  Templates use &quot; lazy structural constraints &quot;.  Generics are instantiated at   run-time  by the CLR.  The type checking of  a generic is peformend at the point where the generic is  defined . Generics are  cross-language .  Generics  do not allow  specialization. Generics  do not allow  non-type parameters.  Generics use  subtype constraints .  *http://blogs.msdn.com/branbray/archive/2003/11/19/51023.aspx
Verifiable C++ 以 /clr:safe 編譯,將會試著產生 verifiable assembly 若使用了不安全的語法時,將會得到錯誤訊息 例如,不能使用指標運算 諸如 tempaltes, deterministic destruction 其他功能則是安全的
使用程式庫 使用 managed assembly : #using #using <System.Data.dll> 使用 COM 元件: #import #using <msxml4.dll> 使用 Standard C++ Library : #include #include <iostream>
C++/CLI 的標準化現況 C++/CLI 在 2005 年 12 月已經成為國際標準( ECMA 372 )
Reference Hello C++/CLI http://msdn.microsoft.com/msdnmag/issues/06/00/PureC/default.aspx ECMA-372 : C++/CLI Language Specification  http://www.ecma-international.org/publications/standards/Ecma-372.htm void Nish(char *szBlog) http://blog.voidnish.com/index.php?cat=2 The Code Project http://www.codeproject.com/managedcpp/#C%2B%2B%2FCLI
Thanks

Introduction to C++ over CLI

  • 1.
    C++/CLI 概觀 王建興qing@cs.nthu.edu.tw (Email/MSN) 2007/1/25
  • 2.
    About Qing EducationPh.D. Candidate, Department of Computer Science, National Tsing-Hua University, Taiwan Research interests: distribute network management, mobile agent, VoIP, and p2p networking Software Development Skills Programming languages: 80x86 assembly, C/C++, Java, C# J2EE development and Web programming: EJB, JSP/Servlet Network programming: TCP/IP, socket programming Object Oriented Design/Programming Design Patterns and Software Architecture Distributed Network Management System Peer-to-Peer Networking Book Translation Thinking in Java 2nd Edition, in Traditional Chinese Essential C++, in Traditional Chinese
  • 3.
    Agenda 何謂 C++/CLIC++/CLI 中的型別宣告 Handle vs. Pointer Deterministic Destruction of C++/CLIS C++/CLI 中的陣列 C++/CLI 對 Property 的支援 C++/CLI 的四種編譯模式 Templates 及 Generics C++/CLI 的標準化
  • 4.
    在 C++/CLI 出現前,.NET 上的 C++ 程式員恐怕只有一個字可以形容 悶
  • 5.
    .NET 上的 C++程式員為什麼悶? (1/2) 僅管有了 Managed Extension for C++ ,但卻存在一些對程式員的困擾 不夠流暢優雅的語法 例如:雙底線的關鍵字們 對 CLI 的支援不夠支接 例如:缺乏 for-each 語法,不支援 property C++ 與 .NET 之間的整合不夠好 例如:無法在 CLI 型別上使用 template ,也無法在 C++ 型別上運用 CLI 的機制,例如垃圾回收 *http://www.codeproject.com/managedcpp/cppcliintro01.asp
  • 6.
    .NET 上的 C++程式員為什麼悶? (2/2) 有兩種形式的指標: unmanaged 指標及 managed 指標,其用法及表示容易造成混淆 無法產生 verifiable 的程式碼
  • 7.
    為什麼不學 C# 就好了?多學一個程式語言 C++ 最佳化能力較強 C++ 具備 deterministic destruction (即使編譯為 MSIL ) C++ 程式員在 C++ 的特性支持下,生產力較高 STL template specialization
  • 8.
    好消息是… .Net 上的C++ 程式員不用再悶了
  • 9.
    C++/CLI 所帶來的拯救 優雅流暢的語法對 CLI 的直接支援 property ,泛型,垃圾回收等機制全都有了直接的支援 在 unmanaged classes 上這些機制也都適用 妥善的橋接 .NET 與 C++ 透過 C++/CLI 編譯出來的程式碼,是 fully verifiable *http://www.codeproject.com/managedcpp/cppcliintro01.asp
  • 10.
    何謂 C++/CLI ?C++ 所支援的靜態物件模型,目標放在對執行檔速度及大小的最佳化上 CLI 則為一支援動態元件編程模型的多階架構 C++/CLI 中的” /” ,代表 C++ 與 CLI 之間的 binding 何謂 C++/CLI 近似說法 1 : C++/CLI 將 C++ 靜態的物件模型繫結 (bind) 至 CLI 的動態元件模型 近似說法 2 : C++/CLI 將 .NET 編程模型整合進到 C++ 中 * Stanley B. Lippman , http://msdn.microsoft.com/msdnmag/issues/06/00/PureC/default.aspx
  • 11.
    C++/CLI 就是 C++X CLI 確定性的記憶體管理 Template Native types STL 、 generic algorithms 指標 複製建構及指派 垃圾收集, finalizer 泛型 Reference 及 value types Interface Verifiability Security Properties, delegates, events 功能強力的 BCL C++ 的特點 CLI 的特點 C++/CLI 發揮 C++ 及 CLI 的相乘效果
  • 12.
  • 13.
    C++ 及 CLI物件模型的比較 物件儲存空間類型多樣化 static, stack, 以及 heap 物件的生命期類型多樣化 static object 有著和程式相同的生命期 stack 上的 object ,則在和 scope({ … }) 的生命期相同 heap 上的 object ,則由程式員自行操控其生命期 物件建構及指派的深層拷貝模型 編譯時,原始碼中必須含括所有動用型別的資訊 物件儲存空間類型單純 Value type 儲存於 stack Reference type 儲存於 managed heap 上 物件的生命期具不確定性 Reference types 須倚靠垃圾收集器回收 物件的指派採淺層參考語義 編譯時,型別資訊由所使用型別的 metadata 提供 C++ 的靜態物件模型 CLI 的動態物件模型
  • 14.
    C++/CLI 中的型別宣告 在C++/CLI 中可宣告下述型別 CLR 的 enumeration type enum class E {…} CLR 的 interface type interface class I {…} CLR 的 value type value class V {…} CLR 的 reference type ref class R {…} C++ 原生類別(過去 C++ 程式員所用) class N {…} 所宣告的型別 宣告語法
  • 15.
    C++ 與 CTS型別的對應 N/A Boolean bool N/A Double long double N/A Double double N/A Single float UInt64 Int64 __int64 UInt32 Int32 long int UInt32 Int32 int, __int32 UInt16 Int16 short int Byte Sbyte char CTS Unsigned Type CTS Signed Type C++ Type
  • 16.
    各型別的用途 原生型別 具備native code 的語義及優點 即使被編譯成 MSIL 亦如此 Reference Type 可被垃圾收集器回收,提供簡便的記憶體管理模式 Value Type 輕量級的型別(例如像整數之類的型別) Interface Type 宣告 CLR 中的介面型別 Enumeration Type 宣告 CLR 中的列舉型別
  • 17.
    例:在 C++/CLI 中宣告Reference Type ref class Qing { public: void sayHello(); }; void Qing::sayHello() { Console::WriteLine(&quot;Hello!&quot;); } 多半宣告在 .h 檔裡 多半宣告在 .pp 檔裡
  • 18.
    Handle vs. PointerC++/CLI 中將 native C++ 型別及 CLI 型別區分開來 C++/CLI 中同樣具備指向 CLI 型別的 ” 指標”,但採用了不同的符號來表示,同時賦予一個不同的名稱,以資區別 這個名稱就是 Handle (相對於 Pointer ) 這個符號就是 ^ (相對於 * ) 建構 native C++ 型別及 CLI 型別的關鍵字也已有所區隔 new :產生 native C++ 型別 gcnew :產生 CLI 型別( gc 意指 garbage collected ,垃圾回收之意)
  • 19.
    透過 Handle 建構並運用CLI 型別 Qing ^ qing = gcnew Qing(); qing->sayHello();
  • 20.
    使用 Pointer 和Handle 的差異 Pointer 用來在 native heap 中配置空間 語法: T* t = new T; 指標指向的位置是穩定的(不受 GC 影響),甚至可以被轉型為 int 倘若未自行釋放記憶體( delete ),則會產生 memory leak Handle 用來在 managed heap 中配置空間 語法: T^ t = gcnew T; GC 會自動釋放位於 managed heap 中的物件所佔用的空間 但程式員仍可針對 managed heap 上的物件呼叫 delete
  • 21.
    不同的兩種物件生成及運用模式 *Kate Gregory,“Moving C++ Applications to the Common Language Runtime”   Use Native Heap    Use Managed Heap   Use Stack ^ and % always * and & never Verifiability d elete delete Free gcnew new Allocate % & Reference ^ * Pointer / Handle Managed Native
  • 22.
    於 Native/CLI 型別中混用Pointer/Handle (1/2) 在 CLI 型別中使用 Handle 在 CLI 型別中使用 Pointer ref class R { private: String ^str; } ref class R { private: std::string* str; }
  • 23.
    於 Native/CLI 型別中混用Pointer/Handle (2/2) 在 Native 型別中使用 Handle gcroot<T> 是用來包裝 System.Runtime.InteropServices.GCHandle 的一個 template class *http://www.codeproject.com/managedcpp/ijw_unmanaged.asp class N { private: gcroot<String^> str;; }
  • 24.
    Tracking Reference Operator% & 之於 pointer ,相當於 % 之於 handle R1 ^pr1 = gcnew R1(); R1 %r1 = * pr1; Console::WriteLine(r1 . ToString()); 不再是 -> 同樣使用 *
  • 25.
    Tracking Reference 的效應array<String^>^ arr = gcnew array<String^>(3); int i = 0; for each(String^% s in arr) s = gcnew String(i++.ToString()); for each(String^ s in arr) Console::WriteLine(s); 執行結果: 0 1 2 array<String^>^ arr = gcnew array<String^>(3); int i = 0; for each(String^ s in arr) s = gcnew String(i++.ToString()); for each(String^ s in arr) Console::WriteLine(s); 執行結果:
  • 26.
    CLI 的 Non-DeterministicFinalization CLI 的垃圾回收機制,會在記憶體傾向不足時,將不再使用被物件佔用的記憶體空間回收再用 當物件佔用的記憶體空間確定被回收前的一刻,該物件的 Finalize() 會被呼叫,此即為 finalization (終始化)動作 Finalization 執行的時機及是否會被執行任誰都說不得準 所以被稱為 non-deterministic finalization
  • 27.
    Disposal 對於記憶體資源而言, non-deterministic finalization 不成問題 但對非記憶體資料(例如資料庫 、 檔案), 面對 non-deterministic finalization 的程式員得多費心才能使程式如預期的運作 對 .NET 而言,慣例上清理此類資料的動作,會定義於名為 Close() 或 Dispose() 的方法中 但這麼一來,程式員得自行呼叫 Close() 或 Dispose() 來進行清理動作
  • 28.
    Deterministic Destruction C++/CLI除了 CLI 上原先就有的 finalizer 之外,還提供了 destructor. ref class R1 { public: R1() {} ~R1() {} protected: !R1() {} }; constructor destructor finalizer * 注意: C++/CLI 使用了 C# 中用來表示 Finalizer 的符號來表示 Destructor
  • 29.
    Finalizer vs. DestructorC++/CLI 中的 Destructor 最終會被編譯成為 Dispose() ,如果用 C# 的相對應程式碼來看的話,會像是: GC::SuppressFinalize() 會要求系統不要呼叫物件的 finalizer() ,避免物件被清理兩次 public void Dispose()//IDisposable::Dispose { GC.SuppressFinalize(this); } *http://www.codeproject.com/managedcpp/cppclidtors.asp *http://msdn2.microsoft.com/en-us/library/system.gc.suppressfinalize.aspx
  • 30.
    Why SuppressFinalize? public class FileStream : Stream { public override void Close() { // Clean up this object: flush data and close file … // There is no reason to Finalize this object now GC.SuppressFinalize(this); } protected override void Finalize() { Close(); // Clean up this object: flush data and close file } // Rest of FileStream methods go here … } *http://www.codeproject.com/managedcpp/cppclidtors.asp 如果程式員自行呼叫了 Close() , 但 GC 又呼叫了 Finalize() 便會引發 Close() 被叫用兩次
  • 31.
    自動進行的 Disposal {SqlConnection conn(connString); } constructor 會被呼叫 destructor 會被自動呼叫 { SqlConnection ^pConn = gcnew SqlConnection(connString); } constructor 會被呼叫 之後就要等待 GC 動作了
  • 32.
    C++/CLI 中的陣列 使用Managed Heap 的 Managed Array ,其基礎型別皆為 System::Array 使用 C++/CLI 的陣列時,可以想像存在一個虛擬的 template http://www.codeproject.com/managedcpp/cppcliarrays.asp namespace stdcli::language { template<typename T, int rank = 1> ref class array : System::Array {}; }
  • 33.
    陣列定義語法 一維陣列語法 多維度陣列語法array<String ^> ^ strArray = gcnew array<String ^>(10); for(int i=0;i<strArray->Length;i++) { strArray[i] = &quot;&quot;+i; Console::WriteLine(strArray[i]); } array<String ^, 3> ^ strArray = gcnew array<String ^, 3>(4, 3, 2); 維度 每個維度的長度
  • 34.
    陣列的初始化 array<String^>^ strarr= gcnew array<String^> {“String1&quot;, “String2&quot;}; array<String^>^ strarr2 = {“String1&quot;, “String2&quot;}; array<Object^,2> ^ objarr = {{“String1&quot;, 1}, {“String2&quot;, 2}};
  • 35.
    參數陣列 C++/CLI 支援參數陣列,參數陣列必須是最後一個參數用…來標示參數陣列 void testParamArray(String ^s, ... array<String ^>^ params) { Console::WriteLine(s+&quot;: &quot;); for(int i=0;i<params->Length;i++) Console::WriteLine(params[i]); } int main(array<System::String ^> ^args) { testParamArray(&quot;Hello&quot;, &quot;qing&quot;); testParamArray(&quot;Hello&quot;, &quot;qing&quot;, &quot;chrisma&quot;); }
  • 36.
    Property 的語法 refclass UserAccount { public: property String^ ID { String^ get(){return id;} virtual void set(String^ value){id = value;} } private: String^ id; }; 就和 C# 的定義方式類似
  • 37.
    Index Property 的語法ref class OnlineUserList{ public: property UserAccount^ User[int] { UserAccount^ get(int idx){return (UserAccount^) alUser[idx];} } OnlineUserList() { alUser = gcnew System::Collections::ArrayList(); } // … private: System::Collections::ArrayList ^alUser; };
  • 38.
    Refernece Type 允許單一繼承多重實作ref class R abstract {}; public ref class R2 : R, IClone, IComparable, IDisposable, IEnumerable { }; 所有 reference type 都繼承自 System::Object 最多繼承一個類別,但可以實作多個介面
  • 39.
    Exception Handling try{ throw gcnew Exception(&quot;qing&quot;); } catch(System::Exception^ e) { Console::WriteLine(e->StackTrace); }
  • 40.
    CLR 編譯模式:共有四種可供選擇 Mix(/clr) 包含 managed/unmanaged code Pure( /clr:pure ) 僅包含 managed code 但仍可使用 #include 並呼叫原生 API Verifiable( clr:safe) 僅包含 verifiable code Managed Extensions for C++( clr:oldSyntax ) 用來編譯舊式的程式碼
  • 41.
    在 VS 2005中選擇編譯模式
  • 42.
    編譯模式之間的關連性 Native CLRCode Data Machine Code CLR Data / Types Native Data / Types MSIL Code Mixed C++ /clr Native C++ Verifiable C++ /clr:safe Pure C++ /clr:pure
  • 43.
    CLR 編譯模式帶來的應用 在C++ 應用程式中直接使用 BCL 或以 .NET 寫成的 library 大幅提高生產力 將 native code 的功能,以 .NET 物件模型的方式對外提供 便於其他 .NET 應用程式使用( ASP.NET )
  • 44.
    決定 Managed/Unmanaged 的交界處C++ managed C++ CRT, STL, etc One call to foo() Hundreds of calls C# or C++ managed C++ CRT, STL, etc One call to foo() Hundreds of calls C# C++ CRT, STL, etc Hundreds of calls C++ One call to foo() One call * Kate Gregory, “ Moving C++ Applications to the Common Language Runtime”
  • 45.
    C++/CLI 支援 Templatetemplate<typename T1, typename T2> class NativeData { public: NativeData(T1 t1) { m_t1 = t1; } void DoStuff(T2 t2) { //... } private: T1 m_t1; }; *http://www.codeproject.com/managedcpp/cppcligenerics.asp
  • 46.
    Tempalte 採用 LazyConstraint (1/2) template<typename T> class Native { public: void Start(int x) { T* t = new T(); t->Bark(x); t->WagTail(); delete t; } }; 如何確定 t 有 Bark() 及 WagTail() 兩 methods 呢? *http://www.codeproject.com/managedcpp/cppcligenerics.asp
  • 47.
    Tempalte 採用 LazyConstraint (2/2) Native<NativeDog> d1; d1.Start(100); Native<NativePig> d2; d2.Start(100); 引發編譯器錯誤: error C2039: 'Bark' : is not a member of 'NativePig' *http://www.codeproject.com/managedcpp/cppcligenerics.asp
  • 48.
    C++/CLI 支援 Genericsgeneric<typename T1, typename T2> ref class GenericData { public: GenericData(T1 t1) { m_t1 = t1; } void DoStuff(T2 t2) { //... } private: T1 m_t1; }; *http://www.codeproject.com/managedcpp/cppcligenerics.asp
  • 49.
    Generics 採用 SubtypeConstraints (1/3) generic<typename T> where T:IDog ref class GenRef { public: void Start(int x) { T t = Activator::CreateInstance<T>(); t->Bark(x); t->WagTail(); delete safe_cast<Object^>(t); } }; 透過限制 T 必須實作的介面來進行約束 *http://www.codeproject.com/managedcpp/cppcligenerics.asp
  • 50.
    Generics 採用 SubtypeConstraints (2/3) ref class ClrDog : IDog { public: virtual void Bark(int Loudness) { Console::WriteLine(&quot;ClrDog::Bark {0}&quot;,Loudness); } virtual void WagTail() { Console::WriteLine(&quot;ClrDog::WagTail&quot;); } }; *http://www.codeproject.com/managedcpp/cppcligenerics.asp
  • 51.
    Generics 採用 SubtypeConstraints (3/3) GenRef<ClrDog^> g1; g1.Start(100); *http://www.codeproject.com/managedcpp/cppcligenerics.asp
  • 52.
    使用 Reference Types於 Template template<typename T> class CLR { public: void Start(int x) { T^ t = gcnew T(); t->Bark(x); t->WagTail(); delete t; } }; CLR<ClrDog> g2; g2.Start(100)
  • 53.
    將 Template 宣告為Reference Type template<typename T> ref class CLR2 { public: void Start(int x) { T^ t = gcnew T(); t->Bark(x); t->WagTail(); delete t; } }; CLR 2 <ClrDog> g 3 ; g 3 .Start(100) Console::WriteLine(g3.GetType()->Name); 執行結果: ClrDog::Bark 100 ClrDog::WagTail CLR2<ClrDog>
  • 54.
    Generics Functions generic<typenameT> where T:IDog void DoAll(T t) { t->Bark(0); t->WagTail(); } *http://www.codeproject.com/managedcpp/cppcligenerics.asp
  • 55.
    Template vs. GenericsTemplates are instantiated at compile-time with the source code. The type checking of a template are performed at the point where the template is defined and instantiated . Tempaltes allow specialization. Templates allow non-type parameters. Templates use &quot; lazy structural constraints &quot;. Generics are instantiated at run-time by the CLR. The type checking of a generic is peformend at the point where the generic is defined . Generics are cross-language . Generics do not allow specialization. Generics do not allow non-type parameters. Generics use subtype constraints . *http://blogs.msdn.com/branbray/archive/2003/11/19/51023.aspx
  • 56.
    Verifiable C++ 以/clr:safe 編譯,將會試著產生 verifiable assembly 若使用了不安全的語法時,將會得到錯誤訊息 例如,不能使用指標運算 諸如 tempaltes, deterministic destruction 其他功能則是安全的
  • 57.
    使用程式庫 使用 managedassembly : #using #using <System.Data.dll> 使用 COM 元件: #import #using <msxml4.dll> 使用 Standard C++ Library : #include #include <iostream>
  • 58.
    C++/CLI 的標準化現況 C++/CLI在 2005 年 12 月已經成為國際標準( ECMA 372 )
  • 59.
    Reference Hello C++/CLIhttp://msdn.microsoft.com/msdnmag/issues/06/00/PureC/default.aspx ECMA-372 : C++/CLI Language Specification http://www.ecma-international.org/publications/standards/Ecma-372.htm void Nish(char *szBlog) http://blog.voidnish.com/index.php?cat=2 The Code Project http://www.codeproject.com/managedcpp/#C%2B%2B%2FCLI
  • 60.