Upload
-
View
1.738
Download
1
Embed Size (px)
DESCRIPTION
本講題將對C++/CLI做一個概觀性的介紹。在C++/CLI之前,.NET平台上的C++開發方案,多半只能選擇採用Managed C++。無可諱言的,Managed C++本身存在一些改善的空間,諸如:語法的優雅與寫作的流暢、對泛型程式設計的支援等等。然而,在著名的C++語言大師Stan Lippman及Herb Shtter加入VC++的團隊中時,重新制定後的C++/CLI卻讓這些問題得到了解決的方案。在Visual Studio 2005中,完全支援C++標準的C++/CLI加入了新語法及語義,針對.NET環境提供了最直接的支援。有了C++/CLI,原先標準的C++能力不僅不會受損,而C++/CLI中的標準擴充,也使得C++在.NET平台上獲得額外的加持。C++/CLI有了.NET的加持,一舉具備了reflection及垃圾回收的能力。.NET所提供的標準類別庫,也都可為其所用。再加上C++/CLI混合了unmanaged及managed兩種執行環境,更使得C++/CLI具備更多效率的空間,同時能夠開發系統層級的應用程式。有了C++/CLI,在.NET平台上的C++程式員如同取得一把新的寶劍。
Citation preview
About QingAbout 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
AgendaAgenda
何謂 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++程式員恐怕只有一個字可以形容
在 C++/CLI出現前, .NET上的 C++程式員恐怕只有一個字可以形容
悶
.NET上的 C++程式員為什麼悶? (1/2).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).NET上的 C++程式員為什麼悶? (2/2)
有兩種形式的指標: unmanaged 指標及 managed指標,其用法及表示容易造成混淆
無法產生 verifiable的程式碼
為什麼不學 C#就好了?為什麼不學 C#就好了?
多學一個程式語言 C++最佳化能力較強 C++具備 deterministic destruction(即使編譯為MSI
L) C++程式員在 C++的特性支持下,生產力較高
STL template specialization
好消息是…好消息是…
.Net上的 C++程式員不用再悶了
C++/CLI所帶來的拯救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中的” /”,代表 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 CLIC++/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的目標就是C++/CLI的目標就是
無接縫式的整合Unmanaged 及 Managed Code
C++ 及 CLI物件模型的比較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中的型別宣告
在 C++/CLI中可宣告下述型別
宣告語法 所宣告的型別
class N {…} C++原生類別(過去 C++程式員所用)
ref class R {…} CLR 的 reference type
value class V {…} CLR 的 value type
interface class I {…}
CLR 的 interface type
enum class E {…} CLR 的 enumeration type
C++ 與 CTS型別的對應C++ 與 CTS型別的對應
C++ Type CTS Signed Type CTS Unsigned Type
char Sbyte Byte
short int Int16 UInt16
int, __int32 Int32 UInt32
long int Int32 UInt32
__int64 Int64 UInt64
float Single N/A
double Double N/A
long double Double N/A
bool Boolean N/A
各型別的用途各型別的用途
原生型別 具備 native code的語義及優點 即使被編譯成MSIL亦如此
Reference Type 可被垃圾收集器回收,提供簡便的記憶體管理模式
Value Type 輕量級的型別(例如像整數之類的型別)
Interface Type 宣告 CLR中的介面型別
Enumeration Type 宣告 CLR中的列舉型別
例:在 C++/CLI中宣告 Reference Type例:在 C++/CLI中宣告 Reference Type
ref class Qing
{
public:
void sayHello();
};
void Qing::sayHello()
{
Console::WriteLine("Hello!");
}
多半宣告在 .h檔裡
多半宣告在 .pp檔裡
Handle vs. PointerHandle 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型別透過 Handle建構並運用 CLI型別
Qing ^qing = gcnew Qing();
qing->sayHello();
使用 Pointer 和 Handle的差異使用 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
不同的兩種物件生成及運用模式不同的兩種物件生成及運用模式
Native Managed
Pointer / Handle * ^
Reference & %
Allocate new gcnew
Free delete delete
Use Native Heap Use Managed
Heap
Use Stack
Verifiability * and & never ^ and % always
*Kate Gregory, “Moving C++ Applications to the Common Language Runtime”
於 Native/CLI型別中混用 Pointer/Handle (1/2)於 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/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 %Tracking Reference Operator %
&之於 pointer,相當於%之於 handle
R1 ^pr1 = gcnew R1();
R1 %r1 = *pr1;
Console::WriteLine(r1.ToString());
不再是 ->同樣使用 *
Tracking Reference的效應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);
執行結果:012
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 FinalizationCLI 的 Non-Deterministic Finalization
CLI的垃圾回收機制,會在記憶體傾向不足時,將不再使用被物件佔用的記憶體空間回收再用
當物件佔用的記憶體空間確定被回收前的一刻,該物件的 Finalize() 會被呼叫,此即為 finalization(終始化)動作
Finalization執行的時機及是否會被執行任誰都說不得準 所以被稱為 non-deterministic finalization
DisposalDisposal
對於記憶體資源而言, non-deterministic finalization不成問題
但對非記憶體資料(例如資料庫、檔案), 面對 non-deterministic finalization的程式員得多費心才能使程式如預期的運作
對 .NET 而言,慣例上清理此類資料的動作,會定義於名為 Close() 或 Dispose()的方法中
但這麼一來,程式員得自行呼叫 Close() 或 Dispose()來進行清理動作
Deterministic DestructionDeterministic Destruction
C++/CLI 除了 CLI上原先就有的 finalizer之外,還提供了 destructor.
ref class R1
{
public:
R1() {}
~R1() {}
protected:
!R1() {}
};
constructor
destructor
finalizer
* 注意: C++/CLI 使用了 C# 中用來表示 Finalizer 的符號來表示 Destructor
Finalizer vs. DestructorFinalizer 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? 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自動進行的 Disposal
{
SqlConnection conn(connString);
}
constructor 會被呼叫
destructor 會被自動呼叫
{
SqlConnection ^pConn = gcnew SqlConnection(connString);
}
constructor 會被呼叫
之後就要等待 GC 動作了
C++/CLI中的陣列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] = ""+i;
Console::WriteLine(strArray[i]);
}
array<String ^, 3>^strArray = gcnew array<String ^, 3>(4, 3, 2);
維度 每個維度的長度
陣列的初始化陣列的初始化
array<String^>^ strarr = gcnew array<String^> {“String1", “String2"};
array<String^>^ strarr2 = {“String1", “String2"};
array<Object^,2> ^ objarr = {{“String1", 1}, {“String2", 2}};
參數陣列參數陣列
C++/CLI支援參數陣列,參數陣列必須是最後一個參數 用…來標示參數陣列
void testParamArray(String ^s, ... array<String ^>^ params)
{
Console::WriteLine(s+": ");
for(int i=0;i<params->Length;i++)
Console::WriteLine(params[i]);
}
int main(array<System::String ^> ^args)
{
testParamArray("Hello", "qing");
testParamArray("Hello", "qing", "chrisma");
}
Property的語法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的語法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 允許單一繼承多重實作Refernece Type 允許單一繼承多重實作
ref class R abstract {};
public ref class R2 : R, IClone, IComparable, IDisposable, IEnumerable
{
};
所有 reference type都繼承自 System::Object
最多繼承一個類別,但可以實作多個介面
Exception HandlingException Handling
try
{
throw gcnew Exception("qing");
}
catch(System::Exception^ e)
{
Console::WriteLine(e->StackTrace);
}
CLR編譯模式:共有四種可供選擇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中選擇編譯模式在 VS 2005中選擇編譯模式
編譯模式之間的關連性編譯模式之間的關連性
Native CLR
Co
de
Da
ta
MachineCode
CLRData / Types
NativeData / Types
MSILCode
Mixed C++/clrNative C++
Verifiable C++/clr:safe
Pure C++/clr:pure
CLR編譯模式帶來的應用CLR編譯模式帶來的應用
在 C++應用程式中直接使用 BCL 或以 .NET 寫成的library 大幅提高生產力
將 native code的功能,以 .NET物件模型的方式對外提供 便於其他 .NET應用程式使用( ASP.NET)
決定Managed/Unmanaged的交界處決定Managed/Unmanaged的交界處
C++managed
C++ CRT, STL, etcOne call to foo()
Hundreds of calls
C# or C++managed
C++ CRT, STL, etcOne call to foo()
Hundreds of calls
C# C++ CRT, STL, etcHundreds of calls
C++One call to foo()
One call
* Kate Gregory, “Moving C++ Applications to the Common Language Runtime”
C++/CLI支援 TemplateC++/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)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)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支援 GenericsC++/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)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)Generics採用 Subtype Constraints (2/3)
ref class ClrDog : IDog
{
public:
virtual void Bark(int Loudness)
{
Console::WriteLine("ClrDog::Bark {0}",Loudness);
}
virtual void WagTail()
{
Console::WriteLine("ClrDog::WagTail");
}
};
*http://www.codeproject.com/managedcpp/cppcligenerics.asp
Generics採用 Subtype Constraints (3/3)Generics採用 Subtype Constraints (3/3)
GenRef<ClrDog^> g1;
g1.Start(100);
*http://www.codeproject.com/managedcpp/cppcligenerics.asp
使用 Reference Types 於 Template使用 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宣告為 Reference Type
template<typename T> ref class CLR2
{
public:
void Start(int x)
{
T^ t = gcnew T();
t->Bark(x);
t->WagTail();
delete t;
}
};
CLR2<ClrDog> g3;
g3.Start(100)
Console::WriteLine(g3.GetType()->Name);
執行結果:ClrDog::Bark 100ClrDog::WagTailCLR2<ClrDog>
Generics FunctionsGenerics 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. GenericsTemplate 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 "lazy structural
constraints".
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++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的標準化現況
C++/CLI 在 2005 年 12 月已經成為國際標準( ECMA 372)
ReferenceReference
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
ThanksThanks