• <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>

            C++ Programmer's Cookbook

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

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

             

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

            1. 簡介

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

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

              2. 快速簡潔的本機(jī)interop

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

              如果你仍懷疑這種性能優(yōu)勢,有另外的理由來說明你為什么不得不使用C++/CLI而不是C#或VB.NET——源碼膨脹!下面是一個(gè)C++函數(shù)的例子,它使用了IP幫助者API來枚舉一臺(tái)機(jī)器上的網(wǎng)絡(luò)適配器并且列出與每個(gè)適配器相聯(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)在讓我們看一個(gè)使用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++代碼段(都做一樣的事情-連接兩個(gè)文件的內(nèi)容并把它寫到第三個(gè)文件中)。

              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ī)則來處理它即可。事實(shí)上,這使得在C#中修改代碼比在C++中更乏味-作為一實(shí)例,讓我們修改這些代碼以便即使僅存在一個(gè)輸入文件也能創(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)--這在上面情況下顯然不是個(gè)大問題,但是對于實(shí)際開發(fā)中的修改,這可能是相當(dāng)模糊的且易導(dǎo)致邏輯錯(cuò)誤的。

              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聲明移到了頂部并增加了一個(gè)額外的try塊,就這些。甚至對于象在我的示例代碼片斷中的瑣碎事情,如果所涉及的復(fù)雜性在C++中大大減少,那么,當(dāng)你工作于更大的工程時(shí)你能想象使用棧語義對你的編碼效率千萬的影響。

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

              5) 一個(gè)disposable類繼承層次的C#實(shí)現(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();}
            }
            }

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

              6) 等價(jià)的C++實(shí)現(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接口實(shí)現(xiàn)(我們的類中僅建立了析構(gòu)器)而且最好的部分--類R的析構(gòu)器(Dispose方法)并沒有在該類可能含有的可釋放的成員上調(diào)用Dispose-它沒有必要這樣做,編譯器自動(dòng)為之生成所有的代碼!

            4. 混合類型

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

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

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

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

              在我的例子中,設(shè)想該托管類型R有一個(gè)non-trivial構(gòu)造器和一個(gè)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(); }
            };

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

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

              下面讓我們看相反的情形:一個(gè)CLI類的本機(jī)成員。

            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(); }
            };

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

            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
            }
            };

              這僅是個(gè)相當(dāng)簡單的靈敏指針實(shí)現(xiàn),就象一個(gè)負(fù)責(zé)本機(jī)對象分配/回收的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框架,并用可能的最直接的方式-只需寫你的混合模式代碼并編譯實(shí)現(xiàn)!你可以建立在一個(gè)DLL庫中建立MFC 類,然后建立一個(gè).NET應(yīng)用程序來調(diào)用這個(gè)DLL,還需要把.NET類成員添加到你的MFC類(也實(shí)現(xiàn)可以相反的情況)。

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

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

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

              使用auto_gcroot模板來聲明一個(gè)MD5CryptoServiceProvider成員:

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

              在OnInitDialog過程中,gcnew MD5CryptoServiceProvider成員。

            md5 = gcnew MD5CryptoServiceProvider();

              并且為多行編輯框添加一個(gè)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);
            }

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

            5. 托管模板

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

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

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

              編譯錯(cuò)誤:

            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);

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

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

              編譯錯(cuò):

            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++向你提供了泛型和托管模板,那么請看下面這一個(gè)例子:

            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)生:

              編譯錯(cuò):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);

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

              下列代碼不會(huì)成功編譯:

            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)你下次遇到對泛型的貶謗時(shí),你就知道該怎么做了。

              6. STL/CLR

              當(dāng)大量使用STL的C++開發(fā)者轉(zhuǎn)向.NET1/1.1時(shí)一定感覺非常別扭,他們中的許多可能會(huì)放棄并轉(zhuǎn)回到原來的本機(jī)編碼。從技術(shù)上講,你能結(jié)合.NET類型(using gcroot)使用本機(jī)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以后,他們會(huì)提供STL.NET(或STL/CLR)并可以單獨(dú)從網(wǎng)上下載。

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

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

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

              ·性能--STL.NET通過使用實(shí)現(xiàn)泛型接口的托管模板實(shí)現(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");

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

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

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

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

              它遍歷一個(gè)System::String^容器并把其中的每個(gè)字符串轉(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愛好者們會(huì)對我群起攻擊,而非STL人可能感到厭煩。如果你還沒使用過STL,可以參考有關(guān)資料。

            7. 熟悉的語法

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

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

            char[] arr =new char[128];

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

            int y=arr.Length;

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

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

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

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

            class R
            { ~R(){} }

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

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

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

              請看一下C#泛型語法:

            class R<T>{};

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

            generic<typename T> ref class R{};

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

              8. 結(jié)論

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

            posted on 2008-07-18 22:12 夢在天涯 閱讀(6604) 評論(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)計(jì)

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

            常用鏈接

            隨筆分類

            隨筆檔案

            收藏夾

            Blogs

            c#(csharp)

            C++(cpp)

            Enlish

            Forums(bbs)

            My self

            Often go

            Useful Webs

            Xml/Uml/html

            搜索

            •  

            積分與排名

            • 積分 - 1807508
            • 排名 - 5

            最新評論

            閱讀排行榜

            久久久精品午夜免费不卡| 欧美久久久久久精选9999| 99精品久久久久久久婷婷| 欧美大香线蕉线伊人久久| 国产成人综合久久综合| 久久精品亚洲乱码伦伦中文| 少妇精品久久久一区二区三区| 久久亚洲国产中v天仙www| 婷婷久久五月天| 国产精品欧美久久久久天天影视| 中文字幕久久精品| 国产精品免费久久久久久久久 | 久久人人爽人人人人爽AV| 人妻精品久久无码区| 久久免费观看视频| 一级做a爰片久久毛片16| 99久久国产宗和精品1上映| 久久久精品视频免费观看 | 久久久久久久久久久免费精品| 东方aⅴ免费观看久久av| 91精品国产91久久| 久久亚洲高清观看| 国产精品一区二区久久| 久久精品一本到99热免费| 欧美日韩成人精品久久久免费看| 国产精品久久成人影院| 久久www免费人成看片| 狠狠色丁香婷婷久久综合五月| 久久不见久久见免费影院www日本| 久久精品成人国产午夜| 99精品国产在热久久无毒不卡| 亚洲AV无码1区2区久久| 777午夜精品久久av蜜臀| 国产香蕉久久精品综合网| 国产精品美女久久久网AV| 国产V综合V亚洲欧美久久| 久久久久国产精品嫩草影院| 国产精品伊人久久伊人电影 | 亚洲精品无码久久久影院相关影片| 国产精品久久久99| 欧美国产精品久久高清|