青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

C++ Programmer's Cookbook

{C++ 基礎(chǔ)} {C++ 高級} {C#界面,C++核心算法} {設(shè)計模式} {C#基礎(chǔ)}

C++/CLI:第一流的CLI語言(轉(zhuǎn)載)

 

C++/CLI:第一流的CLI語言
2005-08-25 11:25 作者: 朱先忠編譯 出處: 天極網(wǎng) 責(zé)任編輯:方舟

1. 簡介

  本文并不是為了奉承C++/CLI的輝煌,也不是為了貶低其它如C#或者VB.NET等語言,相反,這只是一個非官方的、以一個喜歡這種語言的非微軟雇員身份來論證C++/CLI有它的自己的唯一的角色,可作為第一流的.NET編程語言。

  一個不斷在新聞組和技術(shù)論壇上出現(xiàn)的問題是,當(dāng)象C#和VB.NET這樣的語言更適合于這種用途時,為什么要使用C++來開發(fā).NET應(yīng)用軟件。通常這樣一些問題后面的評論說是,C++語法是怎樣的復(fù)雜和令人費解,C++現(xiàn)在是怎樣一種過時的語言,還有什么VS.NET設(shè)計者已不再像支持C#和VB.NET一樣繼續(xù)支持C++。其中一些猜疑是完全荒謬的,但有些說法部分正確。希望本文有助于澄清所有這些圍繞C++/CLI語言及其在VS.NET語言層次中的地位的疑惑,神秘和不信任。請記住,本作者既不為微軟工作也沒有從微軟那里取得報酬,只是想從技術(shù)上對C++/CLI作一評判。

  2. 快速簡潔的本機interop

  除了P/Invoke機制可用在另外的象C#或VB.NET這樣的語言外,C++提供了一種獨有的interop機制,稱作C++ interop。C++ interop比P/Invoke直觀得多,因為你只是簡單地#include需要的頭文件,并與需要的庫進行鏈接就能象在本機C++中一樣調(diào)用任何函數(shù)。另外,它比P/Invoke速度快--這是很容易能證明的。現(xiàn)在,可爭辯的是在實際應(yīng)用軟件的開發(fā)中,經(jīng)由C++ interop獲得的性能好處與花在用戶接口交互、數(shù)據(jù)庫存取、網(wǎng)絡(luò)數(shù)據(jù)轉(zhuǎn)儲、復(fù)雜數(shù)學(xué)算法等方面的時間相比可以被忽略,但是事實是在有些情況下,甚至通過每次interop調(diào)用節(jié)省的幾個納秒也能給全局應(yīng)用程序性能/響應(yīng)造成巨大影響,這是絕對不能被忽視的。下面有兩部分代碼片斷(一個是使用P/Invoke機制的C#程序,一個是使用C++ Interop機制的C++程序),我分別記錄了其各自代碼重復(fù)執(zhí)行消耗的時間(毫秒)。不管你如何解釋這些數(shù)據(jù),不管這會對你的應(yīng)用程序產(chǎn)生什么影響,全是你的事。我僅打算事實性地指出,C++代碼的執(zhí)行速度要比C#(其中使用了較多的本機interop調(diào)用)快。

  1) C#程序(使用P/Invoke)

[SuppressUnmanagedCodeSecurity]
[DllImport("kernel32.dll")]
static extern uint GetTickCount();
[SuppressUnmanagedCodeSecurity]
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint GetWindowsDirectory(
[Out] StringBuilder lpBuffer, uint uSize);
static void Test(int x)
{
StringBuilder sb = new StringBuilder(512);
for (int i = 0; i < x; i++)
GetWindowsDirectory(sb, 511);
}
static void DoTest(int x)
{
uint init = GetTickCount();
Test(x);
uint tot = GetTickCount() - init;
Console.WriteLine("Took {0} milli-seconds for {1} iterations",tot, x);
}
static void Main(string[] args)
{
DoTest(50000);DoTest(500000);DoTest(1000000);DoTest(5000000);
Console.ReadKey(true);
}

  2) C++程序(使用C++ Interop)

void Test(int x)
{
TCHAR buff[512];
for(int i=0; i<x; i++)
GetWindowsDirectory(buff, 511);
}
void DoTest(int x)
{
DWORD init = GetTickCount();
Test(x);
DWORD tot = GetTickCount() - init;
Console::WriteLine("Took {0} milli-seconds for {1} iterations",tot, x);
}
int main(array<System::String ^> ^args)
{
DoTest(50000);DoTest(500000);DoTest(1000000);DoTest(5000000);
Console::ReadKey(true);
return 0;
}

  3) 速度比較

重復(fù)次數(shù) C# 程序 C++程序
50,000 61 10
500,000 600 70
1,000,000 1162 140
5,000,000 6369 721
  
  其性能差別真是令人驚愕!這的確是說明為什么要使用C++/CLI的一個好理由,如果你在使用本機interop進行開發(fā),那么性能!完全由于性能,我就將被迫借助本機interop來實現(xiàn)并非基于web的.NET應(yīng)用程序。當(dāng)然,為什么我想要使用.NET來開發(fā)需要大量本機interop技術(shù)的應(yīng)用程序完全是另外一個問題。

  如果你仍懷疑這種性能優(yōu)勢,有另外的理由來說明你為什么不得不使用C++/CLI而不是C#或VB.NET——源碼膨脹!下面是一個C++函數(shù)的例子,它使用了IP幫助者API來枚舉一臺機器上的網(wǎng)絡(luò)適配器并且列出與每個適配器相聯(lián)系的所有IP地址。

  4) 枚舉n/w適配器的C++代碼

void ShowAdapInfo()
{
PIP_ADAPTER_INFO pAdapterInfo = NULL;
ULONG OutBufLen = 0;
//得到需要的緩沖區(qū)大小
if(GetAdaptersInfo(NULL,&OutBufLen)==ERROR_BUFFER_OVERFLOW)
{
int divisor = sizeof IP_ADAPTER_INFO;
#if _MSC_VER >= 1400
if( sizeof time_t == 8 ) divisor -= 8;
#endif
pAdapterInfo = new IP_ADAPTER_INFO[OutBufLen/divisor];
//取得適配器信息
if( GetAdaptersInfo(pAdapterInfo, &OutBufLen) != ERROR_SUCCESS )
{//調(diào)用失敗 }
else
{
int index = 0;
while(pAdapterInfo)
{
Console::WriteLine(gcnew String(pAdapterInfo->Description));
Console::WriteLine("IP Address list : ");
PIP_ADDR_STRING pIpStr = &pAdapterInfo->IpAddressList;
while(pIpStr)
{
Console::WriteLine(gcnew tring(pIpStr->IpAddress.String));
pIpStr = pIpStr->Next;
}
pAdapterInfo = pAdapterInfo->Next;
Console::WriteLine();
}
}
delete[] pAdapterInfo;
}
}

  現(xiàn)在讓我們看一個使用P/Invoke的C#版本。

  5) 使用P/Invoke技術(shù)的C#版本

const int MAX_ADAPTER_NAME_LENGTH = 256;
const int MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
const int MAX_ADAPTER_ADDRESS_LENGTH = 8;
const int ERROR_BUFFER_OVERFLOW = 111;
const int ERROR_SUCCESS = 0;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct IP_ADDRESS_STRING
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string Address;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct IP_ADDR_STRING
{
public IntPtr Next;
public IP_ADDRESS_STRING IpAddress;
public IP_ADDRESS_STRING Mask;
public Int32 Context;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct IP_ADAPTER_INFO
{
public IntPtr Next;
public Int32 ComboIndex;
[MarshalAs(UnmanagedType.ByValTStr,
SizeConst = MAX_ADAPTER_NAME_LENGTH + 4)]
public string AdapterName;
[MarshalAs(UnmanagedType.ByValTStr,
SizeConst = MAX_ADAPTER_DESCRIPTION_LENGTH + 4)]
public string AdapterDescription;
public UInt32 AddressLength;
[MarshalAs(UnmanagedType.ByValArray,
SizeConst = MAX_ADAPTER_ADDRESS_LENGTH)]
public byte[] Address;
public Int32 Index;
public UInt32 Type;
public UInt32 DhcpEnabled;
public IntPtr CurrentIpAddress;
public IP_ADDR_STRING IpAddressList;
public IP_ADDR_STRING GatewayList;
public IP_ADDR_STRING DhcpServer;
public bool HaveWins;
public IP_ADDR_STRING PrimaryWinsServer;
public IP_ADDR_STRING SecondaryWinsServer;
public Int32 LeaseObtained;
public Int32 LeaseExpires;
}
[DllImport("iphlpapi.dll", CharSet = CharSet.Ansi)]
public static extern int GetAdaptersInfo(IntPtr pAdapterInfo, ref int pBufOutLen);
static void ShowAdapInfo()
{
int OutBufLen = 0;
//得到需要的緩沖區(qū)大小
if( GetAdaptersInfo(IntPtr.Zero, ref OutBufLen) ==
ERROR_BUFFER_OVERFLOW )
{
IntPtr pAdapterInfo = Marshal.AllocHGlobal(OutBufLen);
//取得適配器信息
if( GetAdaptersInfo(pAdapterInfo, ref OutBufLen) != ERROR_SUCCESS )
{ //調(diào)用失敗了 }
else{
while(pAdapterInfo != IntPtr.Zero)
{
IP_ADAPTER_INFO adapinfo =
(IP_ADAPTER_INFO)Marshal.PtrToStructure(
pAdapterInfo, typeof(IP_ADAPTER_INFO));
Console.WriteLine(adapinfo.AdapterDescription);
Console.WriteLine("IP Address list : ");
IP_ADDR_STRING pIpStr = adapinfo.IpAddressList;
while (true){
Console.WriteLine(pIpStr.IpAddress.Address);
IntPtr pNext = pIpStr.Next;
if (pNext == IntPtr.Zero)
break;
pIpStr = (IP_ADDR_STRING)Marshal.PtrToStructure(
pNext, typeof(IP_ADDR_STRING));
}
pAdapterInfo = adapinfo.Next;
Console.WriteLine();
}
}
Marshal.FreeHGlobal(pAdapterInfo);
}
}


3. 棧語義和確定性的析構(gòu)

  C++經(jīng)由棧語義模仿給了我們確定性的析構(gòu)。簡言之,棧語義是Dispose模式的良好的語法替代品。但是它在語義上比C# using塊語法更直觀些。請看下列的C#和C++代碼段(都做一樣的事情-連接兩個文件的內(nèi)容并把它寫到第三個文件中)。

  1) C#代碼--使用塊語義

public static void ConcatFilestoFile(String file1, String file2, String outfile)
{
String str;
try{
using (StreamReader tr1 = new StreamReader(file1))
{
using (StreamReader tr2 = new StreamReader(file2))
{
using (StreamWriter sw = new StreamWriter(outfile))
{
while ((str = tr1.ReadLine()) != null)
sw.WriteLine(str);
while ((str = tr2.ReadLine()) != null)
sw.WriteLine(str);
}
}
}
}
catch (Exception e)
{ Console.WriteLine(e.Message); }
}

  2) C++代碼--棧語義

static void ConcatFilestoFile(String^ file1, String^ file2, String^ outfile)
{
String^ str;
try{
StreamReader tr1(file1);
StreamReader tr2(file2);
StreamWriter sw(outfile);
while(str = tr1.ReadLine())
sw.WriteLine(str);
while(str = tr2.ReadLine())
sw.WriteLine(str);
}
catch(Exception^ e)
{ Console::WriteLine(e->Message); }
}

  C#代碼與相等的C++ 代碼相比不僅免不了冗長,而且using塊語法讓程序員自己明確地指定他想在哪兒調(diào)用Dispose(using塊的結(jié)束處),而使用C++/CLI的棧語義,只需讓編譯器使用常規(guī)的范圍規(guī)則來處理它即可。事實上,這使得在C#中修改代碼比在C++中更乏味-作為一實例,讓我們修改這些代碼以便即使僅存在一個輸入文件也能創(chuàng)建輸出文件。請看下面修改后的C#和C++代碼。

  3) 修改后的C#代碼

public static void ConcatFilestoFile(String file1, String file2, String outfile)
{
String str;
try{
using (StreamWriter sw = new StreamWriter(outfile))
{
try{
using (StreamReader tr1 = new StreamReader(file1))
{
while ((str = tr1.ReadLine()) != null)
sw.WriteLine(str);
}
}
catch (Exception) { }
using (StreamReader tr2 = new StreamReader(file2))
{
while ((str = tr2.ReadLine()) != null)
sw.WriteLine(str);
}
}
}
catch (Exception e){ }
}

  把針對StreamWriter的using塊放到頂層需要重新調(diào)整using塊結(jié)構(gòu)--這在上面情況下顯然不是個大問題,但是對于實際開發(fā)中的修改,這可能是相當(dāng)模糊的且易導(dǎo)致邏輯錯誤的。

  4) 修改后的C++代碼

static void ConcatFilestoFile(String^ file1, String^ file2, String^ outfile)
{
String^ str;
try{
StreamWriter sw(outfile);
try{
StreamReader tr1(file1);
while(str = tr1.ReadLine())
sw.WriteLine(str);
}
catch(Exception^){}
StreamReader tr2(file2);
while(str = tr2.ReadLine())
sw.WriteLine(str);
}
catch(Exception^){}
}

  這樣不是比在C#中的做更容易些嗎?我恰好把StreamWriter聲明移到了頂部并增加了一個額外的try塊,就這些。甚至對于象在我的示例代碼片斷中的瑣碎事情,如果所涉及的復(fù)雜性在C++中大大減少,那么,當(dāng)你工作于更大的工程時你能想象使用棧語義對你的編碼效率千萬的影響。

  還不確信?好,讓我們看一下成員對象和它們的析構(gòu)吧。Imagine CLI GC類R1和R2,二者都實現(xiàn)了Idisposable接口且都有函數(shù)F(),還有一個CLI GC類R,它有R1和R2成員和一個函數(shù)F()-它內(nèi)部地調(diào)用R1和R2上的F()成員函數(shù)。讓我們先看C#實現(xiàn)。

  5) 一個disposable類繼承層次的C#實現(xiàn)

class R1 : IDisposable{
public void Dispose() { }
public void F() { }
}
class R2 : IDisposable{
public void Dispose() { }
public void F() { }
}
class R : IDisposable{
R1 m_r1 = new R1();
R2 m_r2 = new R2();
public void Dispose()
{
m_r1.Dispose();
m_r2.Dispose();
}
public void F()
{
m_r1.F();
m_r2.F();
}
public static void CallR()
{
using(R r = new R())
{r.F();}
}
}

  這里有幾件事情要做:必須為每個disposable 類手工實現(xiàn)IDisposable接口,對于具有成員R1和R2的類R,Dispose方法也需要調(diào)用成員類上的Dispose。現(xiàn)在讓我們分析上面幾個類的C++實現(xiàn)。

  6) 等價的C++實現(xiàn)

ref class R1
{
public:
~R1(){}
void F(){}
};
ref class R2
{
public:
~R2(){}
void F(){}
};
ref class R
{
R1 m_r1;
R2 m_r2;
public:
~R(){}
void F()
{
m_r1.F();
m_r2.F();
}
static void CallR()
{
R r;
r.F();
}
};

  注意,這里不再有手工的Idisposable接口實現(xiàn)(我們的類中僅建立了析構(gòu)器)而且最好的部分--類R的析構(gòu)器(Dispose方法)并沒有在該類可能含有的可釋放的成員上調(diào)用Dispose-它沒有必要這樣做,編譯器自動為之生成所有的代碼!

4. 混合類型

  我們知道,C++支持本機類型-總是如此;C++支持CLI類型-本文正是特別強調(diào)這一點;它還支持混合類型-具有CLI成員的本機類型和具有本機成員的CLI類型!請盡管考慮所有你能的可能需求。

  注意,談到Whidbey,混合類型實現(xiàn)還不完整;就我從Brandon,Herb和Ronald發(fā)表的材料的理解得知,存在這種相當(dāng)酷的類型--統(tǒng)一模型,它將在Orcas中實現(xiàn)--你能夠在本機C++堆上new/delete CLI類型,而且也能夠在CLI堆上gcnew/delete本機類型。但既然這是Whidbey以后的東西,本文不討論統(tǒng)一模型。

  在我談?wù)撃愫螘r使用混合類型以前,我想向你說明什么是混合類型。如果你理解混合類型,請?zhí)^下面幾段。這里引用Brandon Bray的說法:"一種混合類型,或者是本機類ref類(需要有對象成員),或者是通過聲明或繼承被分配在垃圾回收堆或本機堆上的。"因此如果你有一個托管類型或者有一個有托管成員的本機類型,你就有了一個混合類型。VC++ Whidbey不直接支持混合類型(統(tǒng)一類型模型是一種Whidbey之后的概念),但是它給我們劃定了實現(xiàn)混合類型的條件。讓我們開始討論包含托管成員的本機類型。

ref class R
{
public:
void F(){}
//假定 non-trivial ctor/dtor
R(){}
~R(){}
};

  在我的例子中,設(shè)想該托管類型R有一個non-trivial構(gòu)造器和一個non-trivial析構(gòu)器。

class Native
{
private:
gcroot<R^> m_ref;
public:
Native():
m_ref(gcnew R()){}
~Native()
{ delete m_ref; }
void DoF()
{ m_ref->F(); }
};

  既然,我不能在我的類中擁有一個R成員,我使用了gcroot模板類(在gcroot.h中聲明,但是你要用"#include vcclr.h"),它包裝了System::Runtime::InteropServices::GCHandle結(jié)構(gòu)。它是個象類一樣的靈敏指針,它重載了運算符->以返回用作模板參數(shù)的托管類型。因此在上面類中,我可以使用m_ref,就好象我已經(jīng)聲明它是R^,而且你能在DoF函數(shù)中看到這正在起作用。實際上你可以節(jié)省delete,這可以通過使用auto_gcroot(類似于std::auto_ptr,在msclr\auto_gcroot.h文件中聲明)代替gcroot來實現(xiàn)。下面是一個更好些的使用auto_gcroot的實現(xiàn)。

class NativeEx
{
private:
msclr::auto_gcroot<R^> m_ref;
public:
NativeEx() : m_ref(gcnew R()){}
void DoF()
{ m_ref->F(); }
};

  下面讓我們看相反的情形:一個CLI類的本機成員。

ref class Managed
{
private:
Native* m_nat;
public:
Managed():m_nat(new Native()){ }
~Managed()
{ delete m_nat; }
!Managed()
{ delete m_nat;
#ifdef _DEBUG
throw gcnew Exception("Oh, finalizer got called!");
#endif
}
void DoF()
{ m_nat->DoF(); }
};

  我不能定義一個Native對象來作為一個ref類成員,因此需要使用一個Native*對象來代替。我在構(gòu)造器中new該Native對象,然后在析構(gòu)器和finalizer中delete它。如果你運行該工程的調(diào)試版,在執(zhí)行到finalizer時將拋出一個異常- 因此開發(fā)者可以馬上添加一個對delete的調(diào)用或為他的CLI類型使用棧語義技術(shù)。奇怪的是,庫開發(fā)小組沒有建立一個gcroot的反向?qū)崿F(xiàn)-但這不是個大問題,我們可以自己寫。

template<typename T> ref class nativeroot
{
T* m_t;
public:
nativeroot():m_t(new T){}
nativeroot(T* t):m_t(t){}
T* operator->()
{ return m_t; }
protected:
~nativeroot()
{ delete m_t; }
!nativeroot()
{
delete m_t;
#ifdef _DEBUG
throw gcnew Exception("Uh oh, finalizer got called!");
#endif
}
};

  這僅是個相當(dāng)簡單的靈敏指針實現(xiàn),就象一個負(fù)責(zé)本機對象分配/回收的ref類。不管怎樣,借助nativeroot模板類,我們可以如下修改托管類:

ref class ManagedEx
{
private:
nativeroot<Native> m_nat;
public:
void DoF()
{ m_nat->DoF(); }
};

  好,關(guān)于混合類型的最大問題是什么呢?你可能問。最大問題是,現(xiàn)在你能混合使用你的MFC、ATL、WTL、STL代碼倉庫和.NET框架,并用可能的最直接的方式-只需寫你的混合模式代碼并編譯實現(xiàn)!你可以建立在一個DLL庫中建立MFC 類,然后建立一個.NET應(yīng)用程序來調(diào)用這個DLL,還需要把.NET類成員添加到你的MFC類(也實現(xiàn)可以相反的情況)。

  作為一例,設(shè)想你有一MFC對話框--它通過一個多行的編輯框接受來自用戶的數(shù)據(jù)-現(xiàn)在,你有一新的要求-顯示一個只讀編輯框,它將顯示當(dāng)前在該多行編輯框中文本的md5哈希結(jié)果。你的隊友正在悲嘆他們將必須花費幾個小時鉆研crypto API,而你的上司在擔(dān)憂你們可能必須要買一個第三方加密庫;那正是你在他們面前樹立形象的時候,你宣布你將在15分鐘內(nèi)做完這項任務(wù)。下面是解決的辦法:

  添加一個新的編輯框到你的對話框資源中,并且添加相應(yīng)的DDX變量。選擇/clr編譯模式并且添加下列代碼到你的對話框的頭文件中:

#include <msclr\auto_gcroot.h>
using namespace System::Security::Cryptography;

  使用auto_gcroot模板來聲明一個MD5CryptoServiceProvider成員:

protected:
msclr::auto_gcroot<MD5CryptoServiceProvider^> md5;

  在OnInitDialog過程中,gcnew MD5CryptoServiceProvider成員。

md5 = gcnew MD5CryptoServiceProvider();

  并且為多行編輯框添加一個EN_CHANGE處理器:

void CXxxxxxDlg::OnEnChangeEdit1()
{
using namespace System;
CString str;
m_mesgedit.GetWindowText(str);
array<Byte>^ data = gcnew array<Byte>(str.GetLength());
for(int i=0; i<str.GetLength(); i++)
data[i] = static_cast<Byte>(str[i]);
array<Byte>^ hash = md5->ComputeHash(data);
CString strhash;
for each(Byte b in hash)
{
str.Format(_T("%2X "),b);
strhash += str;
}
m_md5edit.SetWindowText(strhash);
}

  這里使用了混合類型:一個本機Cdialog派生類,該類含有一個MD5CryptoServiceProvider成員(CLI類型)。你可以輕易地試驗相反的情況(如早期的代碼片斷已顯示的)——可以建立一個Windows表單應(yīng)用程序而且可能想利用一個本機類庫--這不成問題,使用上面定義的模板nativeroot即可。

5. 托管模板

  也許你對泛型的概念已很清楚了,它幫助你避免進入C++的模板夢魘,它是實現(xiàn)模板的最佳方式,等等。好,假設(shè)這些全部正確,C++/CLI支持泛型就象任何其它CLI語言一樣-但是它有而其它一些CLI語言還沒有的是它還支持托管模板-也就是模板化的ref和value類。如果你以前從未使用過模板,你不能一下欣賞這么多優(yōu)點,但是如果你有模板使用背景而且你已發(fā)現(xiàn)了泛型中存在的可能限制你編碼的方式,托管模板將會大大減輕你的負(fù)擔(dān)。你能聯(lián)合使用泛型和模板- 事實上有可能用一個托管類型的模板參數(shù)來實例化一個泛型類型(盡管相反的情形是不可能的,因為運行時刻實例化由泛型所用)。STL.NET (或STL/CLR)以后討論,請很好地利用泛型和托管模板的混合編程吧。

  泛型使用的子類型約束機制將防止你寫出下面的代碼:

generic<typename T> T Add(T t1, T t2)
{ return t1 + t2; }

  編譯錯誤:

error C2676: binary ’+’ : ’T’ does not define this operator or a conversion to a type acceptable to the predefined operator

  現(xiàn)在請看相應(yīng)的模板版本:

template<typename T> T Add(T t1, T t2)
{ return t1 + t2; }

  那么就可以這樣做:

int x1 = 10, x2 = 20;
int xsum = Add<int>(x1, x2);

  還可以這樣做:

ref class R
{
int x;
public:
R(int n):x(n){}
R^ operator+(R^ r)
{ return gcnew R(x + r->x); }
};
//...
R^ r1 = gcnew R(10);
R^ r2 = gcnew R(20);
R^ rsum = Add<R^>(r1, r2);

  這在一個象int的本機類型以及一個ref類型(只要ref類型有一個+運算符)情況下都能工作良好。這個泛型缺點不是一個調(diào)試錯誤或缺陷-它是設(shè)計造成的。泛型的實例化是在運行時通過調(diào)用配件集實現(xiàn)的,因此編譯器不能確知一特定操作能被施行于一個泛型參數(shù),除非它匹配一個子類型約束,因此編譯器在定義泛型時解決這個問題。當(dāng)你使用泛型時的另外一個妨礙是,它不會允許你使用非類型參數(shù)。下列泛型類定義不會編譯:

generic<typename T, int x> ref class G{};

  編譯錯:

error C2978: syntax error : expected ’typename’ or ’class’; found type ’int’; non-type parameters are not supported in generics

  與托管模板相比較:

template<typename T, int x = 0> ref class R{};

  如果你開始感激C++向你提供了泛型和托管模板,那么請看下面這一個例子:

template<typename T> ref class R{
public:
void F()
{ Console::WriteLine("hey"); }
};
template<> ref class R<int>
{
public:
void F()
{ Console::WriteLine("int"); }
};

  你不能用泛型這樣編碼;否則,將產(chǎn)生:

  編譯錯:error C2979: explicit specializations are not supported in generics

  但可以在繼承鏈中混合使用模板和泛型:

generic<typename T> ref class Base
{
public:
void F1(T){}
};
template<typename T> ref class Derived : Base<T>
{
public:
void F2(T){}
};
//...
Derived<int> d;
d.F1(10);
d.F2(10);

  最后,你不能從一個泛型參數(shù)類型派生一個泛型類。

  下列代碼不會成功編譯:

generic<typename T> ref class R : T
{};

  error C3234: a generic class may not derive from a generic type parameter

  模板讓你這樣做(好像你還不知道這些):

ref class Base{
public:
void F(){}
};
generic<typename T> ref class R : T
{};
//...
R<Base> r1;
r1.F();

  這樣,當(dāng)你下次遇到對泛型的貶謗時,你就知道該怎么做了。

  6. STL/CLR

  當(dāng)大量使用STL的C++開發(fā)者轉(zhuǎn)向.NET1/1.1時一定感覺非常別扭,他們中的許多可能會放棄并轉(zhuǎn)回到原來的本機編碼。從技術(shù)上講,你能結(jié)合.NET類型(using gcroot)使用本機STL,但是產(chǎn)生的結(jié)果代碼可能相當(dāng)?shù)托В挥谜f是丑陋了:

std::vector< gcroot<IntPtr> >* m_vec_hglobal;
//...
for each(gcroot<IntPtr> ptr in *m_vec_hglobal)
{ Marshal::FreeHGlobal(ptr);}

  大概VC++小組考慮到了這些并決定在Whidbey以后,他們會提供STL.NET(或STL/CLR)并可以單獨從網(wǎng)上下載。

  你可能問為什么?Stan Lippman,在他的MSDN文章(STL.NET Primer)中給出了3條原因:

  ·可擴展性--STL設(shè)計把算法和容器隔離到自己的應(yīng)用空間-也就是你可以有一組容器和一組算法,并且你能在任何一個容器上使用這些算法;同時你能在任何一個算法中使用這些容器。因此,如果你添加一種新的算法,你能在任何一種容器中使用它;同樣,一個新的容器也可以與現(xiàn)有算法配合使用。

  ·統(tǒng)一性--所有核心C++開發(fā)者集中在一起,匯集起他們精妙的STL專長,再使用他們的專長則輕車熟路。要較好地使用STL需要花費時間-然而一旦你掌握了它,你就有了在.NET世界中使用你的技巧的明顯優(yōu)勢。不是嗎?

  ·性能--STL.NET通過使用實現(xiàn)泛型接口的托管模板實現(xiàn)。并且既然它的核心已用C++和托管模板編碼,可以期盼它比在BCL上使用的泛型容器更具有性能優(yōu)勢。

  使用過STL的人不需要任何示范,所以下面代碼有益于以前沒有使用過STL的人。

vector<String^> vecstr;
vecstr.push_back("wally");
vecstr.push_back("nish");
vecstr.push_back("smitha");
vecstr.push_back("nivi");
deque<String^> deqstr;
deqstr.push_back("wally");
deqstr.push_back("nish");
deqstr.push_back("smitha");
deqstr.push_back("nivi");

  我使用了兩個STL.NET容器-vector和deque,并裝滿兩個容器,使其看起來相同(在兩個容器中都使用了push_back)。現(xiàn)在,我將在兩個容器上使用replace算法-我們再次看到,這些代碼是很相同的。

replace(vecstr.begin(), vecstr.end(),
gcnew String("nish"), gcnew String("jambo"));
replace(deqstr.begin(), deqstr.end(),
gcnew String("nish"), gcnew String("chris"));

  這里特別要注意的是我使用了"同樣"的算法--replace并在兩個不同STL容器上使用相同的函數(shù)調(diào)用。這是當(dāng)Stan談及"可擴展性"時的意思。下面我用一個簡單函數(shù)來證明:

template<typename ForwardIterator> void Capitalize(
ForwardIterator first,F(xiàn)orwardIterator end)
{
for(ForwardIterator it = first; it < end; it++)
*it = (*it)->ToUpper();
}

  它遍歷一個System::String^容器并把其中的每個字符串轉(zhuǎn)化為大寫。

Capitalize(vecstr.begin(), vecstr.end());
Capitalize(deqstr.begin(), deqstr.end());
for(vector<String^>::iterator it = vecstr.begin();
it < vecstr.end(); it++)
Console::WriteLine(*it);
Console::WriteLine();
for(deque<String^>::iterator it = deqstr.begin();
it < deqstr.end(); it++)
Console::WriteLine(*it);

  上面我的算法能夠與vector和deque容器工作良好。至此,不再細(xì)談;否則,guru站上的STL愛好者們會對我群起攻擊,而非STL人可能感到厭煩。如果你還沒使用過STL,可以參考有關(guān)資料。

7. 熟悉的語法

  開發(fā)者經(jīng)常迷戀他們所用的編程語言,而很少是出于實用的目的。還記得當(dāng)微軟宣布不再為VB6提供官方支持時,VB6人的反抗嗎?非VB6人對此可能非常震驚,而老道的VB6人早已為他們的語言作好葬禮準(zhǔn)備了。事實上,如果VB.NET從來沒被發(fā)明,多數(shù)VB6人將會離開.NET,因為C#將會對他們非常陌生,而它的祖先就是C++。如今,許多VB.NET人可能已經(jīng)轉(zhuǎn)向了C#,但是他們不會從VB6直接轉(zhuǎn)向C#;VB.NET起到一個橋梁作用讓他們的思想脫離開原來VB6思想。相應(yīng)地,如果微軟僅發(fā)行VB.NET(而沒有C#),那么.NET可能成為了新的面向?qū)ο骎B,且?guī)в幸粋€更大的類庫-C++社團的人可能對此嗤之以鼻-他們甚至不會麻煩地檢驗.NET基礎(chǔ)類庫。為什么任何使用一種特定語言的開發(fā)者會對另外一個團體的使用另外開發(fā)語言的開發(fā)者嗤之以鼻?這不是我要回答的問題。--要回答該問題也許要先回答為什么有的人喜歡威士忌,有的人喜歡可口可樂,而還有人喜歡牛奶。所有我要說的是,對開發(fā)者來說,語法家族是個大問題。

  你認(rèn)為對于一個具有C++背景的人,下面的代碼具有怎樣的直覺性?

char[] arr =new char[128];

  他/她可能回答的第一件事是,方括號放錯了位置。下面這句又如何?

int y=arr.Length;

  "呀!"-最可能的反映。現(xiàn)在把下面與前面相比較:

char natarr[128];
array<char>^ refarr=gcnew array<char>(128);
int y=refarr->Length;

  請注意聲明一個本機數(shù)組和一個托管數(shù)組時的語法區(qū)別。這里不同的模板形式的語法可視化地告誡開發(fā)者這一事實--refarr并不是典型的C++數(shù)組而且它可能是某種CLI類的派生物(事實上確是如此),所以極有可能可以把方法和屬性應(yīng)用于它。

  C#的finalizer語法最有可能引起轉(zhuǎn)向C#的C++程序員的混淆。請看見下列C#代碼:

class R
{ ~R(){} }

  好,這樣~R看起來象一個析構(gòu)器但實際是個finalizer。為什么?請與下面的C++代碼比較:

ref class R
{
~R(){ }
!R(){ }
};

  這里~R是析構(gòu)器(實際上等價于一個析構(gòu)器的Dispose模式-但對C++人員來說,這它的行為象個析構(gòu)器)而新的!R語法是為finalizer建立的-這樣就不再有混淆而且語法看上去也與本機C++相匹配。

  請看一下C#泛型語法:

class R<T>{};

  再請看一下C++的語法:

generic<typename T> ref class R{};

  曾經(jīng)使用過模板的人馬上就看出這種C++語法,而C#語法不能保證其沒有混淆性且也不直觀。我的觀點是,如果你以前具有C++背景,C++/CLI語法將最貼近于你以前所用。C#(以及J#)看上去象C++,但是還有相當(dāng)多的極為使人煩火的奇怪語義差別而且如果你沒有完全放棄C++,語法差別將永遠(yuǎn)不停地帶給你混亂和挫折。從這種意義上說,我認(rèn)為VB.NET更好些,至少它有自己唯一的語法,所以那些共用C++和VB.NET的人不會產(chǎn)生語法混亂。

  8. 結(jié)論

  最后,至于你用什么語言編程,這可能依賴許多因素——如:你在學(xué)校學(xué)習(xí)的是什么語言,你是用什么語言開發(fā)的現(xiàn)有代碼倉庫,是否你的客戶對你有具體的語言要求等。本文的主要目的是幫助你確定使用C++/CLI的幾個明確的場所--這里,它比另外CLI語言更具有明顯優(yōu)勢。如果你開發(fā)的應(yīng)用程序有90%的使用時間是涉及本機interop,為何還要考慮使用另外的而不是C++?如果你想開發(fā)一個通用集合,為什么僅把你自己限制在泛型上,而不是結(jié)合泛型和模板的優(yōu)勢呢?如果你已經(jīng)用C++工作,為什么還要學(xué)習(xí)一種新的語言?我常覺得象C#和VB.NET這樣的語言總是盡量向開發(fā)者隱藏CLR,而C++不僅讓你品味CLR,甚至可以讓你去親吻CLR! 

posted on 2008-07-18 22:12 夢在天涯 閱讀(6615) 評論(1)  編輯 收藏 引用 所屬分類: Manage c++ /CLI

評論

# re: C++/CLI:第一流的CLI語言(轉(zhuǎn)載) 2011-08-02 02:17 MyChip

好文。謝謝  回復(fù)  更多評論   

公告

EMail:itech001#126.com

導(dǎo)航

統(tǒng)計

  • 隨筆 - 461
  • 文章 - 4
  • 評論 - 746
  • 引用 - 0

常用鏈接

隨筆分類

隨筆檔案

收藏夾

Blogs

c#(csharp)

C++(cpp)

Enlish

Forums(bbs)

My self

Often go

Useful Webs

Xml/Uml/html

搜索

  •  

積分與排名

  • 積分 - 1813040
  • 排名 - 5

最新評論

閱讀排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              午夜久久tv| 樱桃视频在线观看一区| 亚洲美女精品久久| 亚洲欧洲日韩在线| 欧美精品日韩精品| 在线视频欧美日韩精品| 亚洲一区二区三区三| 国产麻豆日韩| 久久九九免费| 欧美高清不卡在线| 亚洲一区二区日本| 久久av最新网址| 亚洲片在线资源| 正在播放亚洲一区| 精品福利免费观看| 亚洲精品色图| 国产一区二区三区久久久| 欧美黄色网络| 国产精品视频xxxx| 欧美高清在线播放| 国产精品成人v| 可以免费看不卡的av网站| 欧美理论视频| 久久久爽爽爽美女图片| 欧美人与性动交α欧美精品济南到| 亚洲一区二区三区在线| 久久久久国产精品一区三寸| 艳妇臀荡乳欲伦亚洲一区| 性色av一区二区三区红粉影视| 亚洲国产精品va在线看黑人动漫 | 99re6这里只有精品| 亚洲一区不卡| 亚洲精品久久久久久一区二区| 亚洲一区二区三区精品在线观看| 亚洲高清资源| 欧美在线一二三区| 亚洲欧洲99久久| 欧美国产在线电影| 蜜臀va亚洲va欧美va天堂| 国产精品久久看| 最近中文字幕日韩精品| 国语自产精品视频在线看| 中文高清一区| 亚洲深夜福利视频| 你懂的成人av| 欧美α欧美αv大片| 国产欧美丝祙| 亚洲专区在线视频| 亚洲永久视频| 欧美午夜精品理论片a级大开眼界| 欧美激情第三页| 在线欧美小视频| 久久久久久久欧美精品| 久久精品一级爱片| 国产乱码精品一区二区三区不卡| 亚洲日本精品国产第一区| 欧美国产日韩视频| 欧美视频成人| 久久久精品2019中文字幕神马| 欧美福利小视频| 亚洲女同在线| 欧美日韩国产区一| 亚洲福利视频免费观看| 激情综合电影网| 欧美在线视频免费观看| 久久gogo国模裸体人体| 国产精品vvv| 亚洲天堂男人| 欧美一区二区三区免费视频| 国产精品a久久久久| 99国产精品久久久| 亚洲精品一区二区三| 欧美成人激情在线| 欧美国产精品专区| 亚洲精品国产无天堂网2021| 麻豆精品视频| 亚洲精品一区久久久久久| 99精品热6080yy久久| 欧美日韩美女在线| 亚洲一区视频在线观看视频| 午夜日韩在线观看| 很黄很黄激情成人| 老司机aⅴ在线精品导航| 欧美国产一区在线| 亚洲深夜激情| 国产日韩三区| 久久夜色精品国产欧美乱| 欧美成人高清| 一区二区三区免费网站| 国产精品美女久久久久久久| 欧美一级网站| 亚洲国产影院| 欧美一区二区三区婷婷月色 | 亚洲欧美日韩成人| 国产资源精品在线观看| 欧美激情国产日韩| 亚洲欧美视频一区二区三区| 噜噜爱69成人精品| 亚洲一区二区三区影院| 狠狠久久婷婷| 国产精品成人一区| 久久永久免费| 亚洲欧洲av一区二区三区久久| 蜜桃av一区二区三区| 亚洲自拍啪啪| 亚洲激情女人| 国产香蕉97碰碰久久人人| 欧美国产欧美综合 | 亚洲精品久久嫩草网站秘色| 欧美一区二区三区视频在线| 亚洲国产欧美一区| 国产精品私拍pans大尺度在线| 久久久久一区| 亚洲欧美日韩国产一区二区三区| 欧美大片91| 久久久九九九九| 亚洲欧美日韩国产一区二区三区| 亚洲电影在线看| 欧美在线视频一区二区| 欧美人成网站| 久久亚洲高清| 欧美中文在线字幕| 亚洲综合首页| 99国产精品99久久久久久粉嫩| 老**午夜毛片一区二区三区| 亚洲一区影院| 一本色道久久综合狠狠躁篇的优点| 欧美性开放视频| 日韩一级在线| 国产精品美女在线| 欧美日韩国产在线观看| 欧美xxx在线观看| 久久婷婷麻豆| 久久综合久久综合久久| 久久国产乱子精品免费女 | 国产精品高潮在线| 欧美人与性动交a欧美精品| 老司机一区二区| 美女黄毛**国产精品啪啪| 久久精品亚洲一区| 久久成人免费| 久久精品女人| 麻豆91精品| 欧美+日本+国产+在线a∨观看| 老色批av在线精品| 欧美成人小视频| 欧美美女日韩| 国产精品超碰97尤物18| 欧美freesex8一10精品| 嫩模写真一区二区三区三州| 亚洲精品日日夜夜| 免费成人在线视频网站| 久久久精品动漫| 久久久无码精品亚洲日韩按摩| 久久人人爽人人爽| 欧美成人激情在线| 欧美色区777第一页| 国产精品成人一区二区| 国产私拍一区| 亚洲国内自拍| 亚洲视频中文字幕| 久久成人18免费观看| 麻豆91精品91久久久的内涵| 欧美国产一区二区在线观看| 亚洲精品视频免费观看| 亚洲一区二区在线| 久久精品一区二区三区四区 | 亚洲欧美日韩国产中文| 久久久www成人免费毛片麻豆| 免费成人你懂的| 欧美午夜一区二区| 一区二区在线视频播放| 亚洲裸体在线观看| 久久爱www.| 91久久精品国产| 亚洲免费小视频| 免费永久网站黄欧美| 欧美婷婷久久| 在线观看91精品国产麻豆| 亚洲天堂成人在线视频| 久久这里只有| 国产精品99久久久久久久vr| 欧美在线视频a| 欧美视频在线观看一区| 在线成人av| 香蕉久久精品日日躁夜夜躁| 欧美激情成人在线| 午夜精品久久久久久久男人的天堂| 美女在线一区二区| 国产视频欧美视频| 亚洲一区精品视频| 亚洲高清激情| 久久久精品久久久久| 国产精品美女黄网| 免费亚洲一区| 欧美在线看片| 欧美精品成人一区二区在线观看 | 欧美主播一区二区三区| 91久久久亚洲精品| 老色批av在线精品|