VC++2005快速構(gòu)建安全的應(yīng)用程序
一、 簡(jiǎn)介
微軟的Visual C++2005發(fā)布版本對(duì)于有志于輕松、迅速地編寫(xiě)安全可靠的應(yīng)用程序的編程愛(ài)好者來(lái)說(shuō)是正確地選擇。正如你所聽(tīng)到的那樣,Visual C++中語(yǔ)言和庫(kù)的新特點(diǎn)使開(kāi)發(fā)安全、可靠的應(yīng)用程序比以前更容易。它即提供了功能強(qiáng)大并且靈活的標(biāo)準(zhǔn)C++,又提供了適于.NET框架下編程的最強(qiáng)大的開(kāi)發(fā)語(yǔ)言。
本文中,我主要探討Visual C++2005發(fā)布版本中部分語(yǔ)言和庫(kù)的新特色,無(wú)論是對(duì)于教學(xué)項(xiàng)目還是大的應(yīng)用工程,這都將幫助你在編寫(xiě)安全可靠的代碼時(shí)提高工作效率。
二、C運(yùn)行時(shí)庫(kù)的安全特點(diǎn)
如果你正在使用Visual C++創(chuàng)建使用C運(yùn)行時(shí)庫(kù)的應(yīng)用程序,你將非常欣慰地了解到現(xiàn)在你所依賴(lài)的許多庫(kù)函數(shù)都有了更安全的版本。對(duì)于需要一個(gè)或多個(gè)緩沖作為輸入的函數(shù)來(lái)說(shuō),已經(jīng)添加了長(zhǎng)度參數(shù),以此讓函數(shù)來(lái)確信不會(huì)超越緩沖的邊界?,F(xiàn)在更多的函數(shù)開(kāi)始對(duì)參數(shù)進(jìn)行合法性檢查,必要時(shí)將調(diào)用無(wú)效參數(shù)處理器。讓我們來(lái)看一些簡(jiǎn)單的例子:
C運(yùn)行時(shí)庫(kù)中最不可靠的是gets函數(shù),它從標(biāo)準(zhǔn)輸入中讀取一行。思考下面的一個(gè)簡(jiǎn)單的例子:
第一行代碼聲明了一個(gè)緩沖變量,并將緩沖區(qū)中的字符初始化設(shè)置為0。為避免意外情況發(fā)生將變量初始化為一個(gè)眾所周知的值是一個(gè)非常好的主意。緊接著,看似清白無(wú)辜的gets函數(shù)從標(biāo)準(zhǔn)的輸入流中讀取一行并且寫(xiě)入到buffer緩沖區(qū)內(nèi)。這有什么錯(cuò)誤嗎?對(duì)于函數(shù)來(lái)說(shuō)C類(lèi)型的數(shù)組不能實(shí)現(xiàn)值傳遞,而是傳遞了指向數(shù)組第一個(gè)元素的指針。所以在函數(shù)看來(lái),char[ ]相當(dāng)于char*指針,并且是一個(gè)不附帶可以決定所指向的緩沖區(qū)大小尺寸的任何額外信息的原始指針。那么gets函數(shù)是怎么作的呢?它假設(shè)緩沖區(qū)無(wú)限大(UINT_MAX 是有精確尺寸的),并將持續(xù)地從輸入流中拷貝字符到緩沖區(qū)內(nèi)。攻擊者可以輕易地使用這個(gè)弱點(diǎn),這種不廣為人知的類(lèi)型錯(cuò)誤被稱(chēng)為緩沖溢出。
很多最初的C運(yùn)行時(shí)庫(kù)函數(shù)遭受同樣的與參數(shù)確認(rèn)有關(guān)的問(wèn)題,并且現(xiàn)在因此受到抨擊。一定要牢記對(duì)于當(dāng)前所要寫(xiě)的應(yīng)用程序來(lái)說(shuō),性能處于次要地位,我們現(xiàn)在生活在一個(gè)安全第一的世界。每一個(gè)受到批評(píng)的函數(shù)已經(jīng)被一個(gè)提供同樣函數(shù)功能,但添加了安全特點(diǎn)的函數(shù)所代替。當(dāng)然,根據(jù)你在已經(jīng)存在的代碼中所使用的舊的庫(kù)函數(shù)的多少,你可能需要花費(fèi)一些時(shí)間來(lái)代碼更替到新的、更安全的版本。這些新的函數(shù)有一個(gè)_s后綴,例如,gets函數(shù)被gets_s函數(shù)代替;遭受抨擊的strcpy函數(shù)被strcpy_s函數(shù)代替。這里有一個(gè)例子:
gets_s函數(shù)有一個(gè)額外的參數(shù),用來(lái)顯示可以寫(xiě)入的最大字符數(shù)量,這里包括一個(gè)NULL終結(jié)符。我使用了sizeof操作符,它能決定數(shù)組的長(zhǎng)度,因?yàn)榫幾g器在編譯時(shí)決定sizeof操作符返回的結(jié)果。記住,sizeof返回操作數(shù)的長(zhǎng)度是以字節(jié)為單位的,所以用數(shù)組長(zhǎng)度來(lái)除以數(shù)組中第一個(gè)元素的長(zhǎng)度將返回?cái)?shù)組中元素的個(gè)數(shù)。這種簡(jiǎn)單的方法可以移植到Unicode編碼下使用_getws_s的情況,這個(gè)函數(shù)也需要得知以字節(jié)為單位的緩沖區(qū)長(zhǎng)度。
正如我所提到的,另外一個(gè)接受安全檢查的常用函數(shù)strcpy函數(shù),就象gets函數(shù)一樣,它沒(méi)有方法來(lái)保證有效的緩沖區(qū)尺寸,所以它只能假定緩沖足夠大來(lái)容納要拷貝的字符串。在程序運(yùn)行時(shí),這將導(dǎo)致不可預(yù)料的行為,正如我所提到的,為了安全需要避免這些不可預(yù)料的行為,這有一個(gè)使用安全的strcpy_s函數(shù)的例子。
有很多原因來(lái)喜歡這個(gè)新的strcpy_s函數(shù)。最明顯的區(qū)別是的額外的、以字節(jié)為單位的參數(shù),它用來(lái)確認(rèn)緩沖區(qū)大小。這允許strcpy_s函數(shù)可以進(jìn)行運(yùn)行時(shí)檢查,以確定寫(xiě)入的字符沒(méi)有超過(guò)目標(biāo)緩沖區(qū)的邊界。還有一些其它的檢查方法來(lái)確定參數(shù)的有效性。在調(diào)試版本中這些檢測(cè)方法,包括顯示調(diào)試報(bào)告的斷言(assertions)方法,如果它們的條件沒(méi)有滿(mǎn)足,它們將顯示調(diào)試報(bào)告。無(wú)論是調(diào)試還是發(fā)行版本,如果一個(gè)特定的條件沒(méi)有得到滿(mǎn)足,一個(gè)無(wú)效的參數(shù)管理器將被調(diào)用,它默認(rèn)的行為是拋出一個(gè)訪(fǎng)問(wèn)沖突來(lái)終止應(yīng)用程序。這非常好的實(shí)現(xiàn)了讓你的應(yīng)用程序持續(xù)運(yùn)行,而不會(huì)產(chǎn)生不可預(yù)期的結(jié)果。當(dāng)然,這種情況完全可以通過(guò)確保類(lèi)似于strcpy_s的函數(shù)不調(diào)用無(wú)效參數(shù)來(lái)避免。
前一個(gè)例子可以通過(guò)新的_countof宏來(lái)進(jìn)一步簡(jiǎn)化,這個(gè)宏移拋開(kāi)了對(duì)有錯(cuò)誤傾向的sizeof操作符的需要。_countof宏返回C類(lèi)型數(shù)組的元素?cái)?shù)量。這個(gè)宏本身對(duì)應(yīng)了一個(gè)模版,如果傳遞一個(gè)原始指針的話(huà),它將無(wú)法通過(guò)編譯。這有一個(gè)例子:
三、使用C++標(biāo)準(zhǔn)庫(kù)
已經(jīng)看了C運(yùn)行時(shí)庫(kù)新增強(qiáng)的安全特性,讓我們來(lái)看一下如何使用C++標(biāo)準(zhǔn)庫(kù)來(lái)進(jìn)一步減少你的代碼中的相似錯(cuò)誤。
當(dāng)你從C運(yùn)行時(shí)庫(kù)轉(zhuǎn)向C++的標(biāo)準(zhǔn)庫(kù),讓你從C++開(kāi)始受益的一個(gè)最有效的方法是使用庫(kù)中的矢量類(lèi)(Vector class)。矢量類(lèi)是C++標(biāo)準(zhǔn)庫(kù)中的一個(gè)模仿一維T數(shù)組的容器類(lèi),這里T可以是事實(shí)上的任何類(lèi)型,你的代碼中使用緩沖區(qū)的地方都可以用矢量對(duì)象來(lái)代替。讓我們來(lái)考慮上一節(jié)的例子,第一個(gè)例子我們使用gets_s函數(shù)來(lái)從標(biāo)準(zhǔn)輸入中讀取一個(gè)行,考慮用下面的代碼代替:
最值得注意的一個(gè)區(qū)別是緩沖區(qū)變量現(xiàn)在是一個(gè)帶有可用方法和操作符的矢量對(duì)象,這個(gè)矢量對(duì)象初始化為10個(gè)字節(jié)長(zhǎng)度,并且構(gòu)造函數(shù)將每個(gè)元素都初始化為0,表達(dá)式&buffer[0]用于得到矢量對(duì)象的第一個(gè)元素的地址,向期待一個(gè)簡(jiǎn)單緩沖區(qū)的C函數(shù)傳遞一個(gè)矢量對(duì)象是一種正確的方法。與sizeof操作符不同的是,所有的容器的尺寸測(cè)量是基于元素的,而不是基與字節(jié)的。例如,矢量的size方法返回的是容器的元素?cái)?shù)量。
在上節(jié)的第二個(gè)例子里,我們使用strcpy_s函數(shù)從源緩沖區(qū)向目標(biāo)緩沖區(qū)拷貝字符。應(yīng)該清楚矢量對(duì)象是如何代替原始的C類(lèi)型的數(shù)組,為了更好的說(shuō)明這一點(diǎn),讓我們來(lái)考慮另外一個(gè)非常有用的C++標(biāo)準(zhǔn)庫(kù)的容器。
提供的basic_string類(lèi)使得字符串在C++中可以作為正常的類(lèi)型來(lái)操作。它提供了各種各樣的重載操作符,為C++程序開(kāi)發(fā)人員提供了自然的編程模式。由于優(yōu)于strcopy_s及其它操作字符串的函數(shù),你應(yīng)該首選basic_string函數(shù)。basic_string以字節(jié)為單位的T類(lèi)型容器。這里T是字符類(lèi)型。C++標(biāo)準(zhǔn)類(lèi)庫(kù)對(duì)于常用的字符類(lèi)型提供類(lèi)型定義。string和wstring中的元素類(lèi)型分別被定義為char和wchar類(lèi)型。下面的例子說(shuō)明basic_string類(lèi)是多么簡(jiǎn)單和安全:
basic_string類(lèi)也提供了你所希望的、常用的字符串操作的方法和操作符,象字符串聯(lián)合及子串的搜索。
最后,C++標(biāo)準(zhǔn)庫(kù)提供了一個(gè)功能非常強(qiáng)大的I/O庫(kù),用來(lái)安全、簡(jiǎn)單地與標(biāo)準(zhǔn)輸入輸出、文件流進(jìn)行交互操作。雖然對(duì)于gets_s函數(shù)來(lái)說(shuō)使用矢量對(duì)象比使用C類(lèi)型的數(shù)組更好,但你可以通過(guò)使用定義的basic_istream 和 basic_ostream類(lèi)進(jìn)一步簡(jiǎn)化。實(shí)際上,你可以書(shū)寫(xiě)簡(jiǎn)單并且類(lèi)型安全的代碼從流中來(lái)讀取包括字符串在內(nèi)的任何類(lèi)型。
cin被定義成一個(gè)basic_istream流,從標(biāo)準(zhǔn)的輸入中提取字符類(lèi)型的元素。wcin是用于wchar_t元素。另一方面,cout被定義為一個(gè)basic_ostream流,用于向標(biāo)準(zhǔn)的輸出流寫(xiě)入操作。正如你能想象的,這種模式比起gets_s和puts函數(shù)來(lái)可以無(wú)限的擴(kuò)展。但是,真正的價(jià)值是在于它非常難以產(chǎn)生讓你的應(yīng)用程序出現(xiàn)安全裂痕的錯(cuò)誤。
四、C++標(biāo)準(zhǔn)庫(kù)中的邊界檢查
默認(rèn)情況,C++標(biāo)準(zhǔn)庫(kù)中大量的容器對(duì)象和迭代對(duì)象沒(méi)有提供邊界檢查。例如,矢量的下標(biāo)操作符通常是一個(gè)比較快,但有潛在的危險(xiǎn)性的操作單獨(dú)元素的方法。如果你正在尋找得到確認(rèn)檢查的操作方法,你可以轉(zhuǎn)向at方法。安全性的增加是以犧牲性能為代價(jià)的。當(dāng)然,絕大情況下性能的降低是可以忽略不計(jì)的,但是對(duì)于性能要求第一位的代碼來(lái)說(shuō),這可能是非常有害的,思考一下下面的簡(jiǎn)單函數(shù):
PrintAll函數(shù)使用了下標(biāo)操作符,因?yàn)樗饕珊瘮?shù)控制,并且可以確認(rèn)是安全的。另一方面,PrintN函數(shù)不能保證索引的有效性,所以它使用了更安全的at方法來(lái)代替。當(dāng)然,并不是所有的容器的存取操作都象這么簡(jiǎn)潔明了。
在保證C++標(biāo)準(zhǔn)庫(kù)的安全特性的同時(shí),Visual C++2005繼續(xù)堅(jiān)持并在很多情況下改進(jìn)了C++標(biāo)準(zhǔn)庫(kù)的運(yùn)行特性,同時(shí)提供了調(diào)節(jié)C++標(biāo)準(zhǔn)庫(kù)安全性的特色。一項(xiàng)受人歡迎的改進(jìn)是在調(diào)試版本中添加了范圍檢查,這對(duì)你的發(fā)行版本性能并不構(gòu)成影響。但這確實(shí)幫助你在調(diào)試階段捕獲越界錯(cuò)誤,甚至是使用傳統(tǒng)上不安全的下標(biāo)操作符的代碼。
不安全的函數(shù),象vector的下標(biāo)操作算子,和其他的函數(shù),象它的front函數(shù),如果不恰當(dāng)?shù)恼{(diào)用,通常會(huì)導(dǎo)致不明確的行為。如果你幸運(yùn)的話(huà),它將很快導(dǎo)致一個(gè)存取沖突,這將使你的應(yīng)用程序崩潰。如果你不那么走運(yùn)的話(huà),它可能默默地持續(xù)運(yùn)轉(zhuǎn)并導(dǎo)致不可預(yù)知的副效應(yīng),這將破壞數(shù)據(jù)并可能被進(jìn)攻者利用。為了保護(hù)你的發(fā)行版本的應(yīng)用程序,Visual C++2005引入了_SECURE_SCL符號(hào),用來(lái)給那些非安全的函數(shù)添加運(yùn)行時(shí)檢查。象下面的代碼那樣在你的應(yīng)用程序中簡(jiǎn)單地定義這個(gè)符號(hào)可以添加額外的實(shí)時(shí)檢查并阻止不確切的行為。
緊記定義這個(gè)符號(hào)對(duì)你的程序沖擊很大,大量的合法的,但是具有潛在非安全的操作將在編譯時(shí)將無(wú)法通過(guò),以避免在運(yùn)行時(shí)出現(xiàn)潛在BUG。思考下面的使用Copy運(yùn)算的例子:
其中,first和last是定義拷貝范圍的迭代參數(shù),destination是輸出迭代參數(shù),指示了目標(biāo)緩沖區(qū)的位置,這個(gè)位置用來(lái)拷貝范圍之內(nèi)的第一個(gè)元素。這里有一個(gè)危險(xiǎn)是destination所對(duì)應(yīng)的目標(biāo)緩沖區(qū)或容器不足夠大,無(wú)法容納所要拷貝的元素。如果Destination是一個(gè)需要安全檢查的迭代參數(shù),類(lèi)似的錯(cuò)誤將被捕獲。但是,這僅僅是一個(gè)假設(shè)。如果destination是一個(gè)簡(jiǎn)單的指針,將無(wú)法保證copy運(yùn)算函數(shù)正確運(yùn)轉(zhuǎn)。這時(shí)當(dāng)然會(huì)想到_SECURE_SCL符號(hào)來(lái)避免這一問(wèn)題,這種情況下,代碼甚至是不能編譯,以此避免任何可能的運(yùn)行時(shí)錯(cuò)誤。就象你想象的那樣,這將需要重寫(xiě)更完美有效的代碼。所以,這是一個(gè)更好的理由支持C++標(biāo)準(zhǔn)庫(kù)容器,避免使用C類(lèi)型數(shù)組。
五、編譯器的安全特點(diǎn)
雖然對(duì)于Visual C++2005來(lái)說(shuō)并不是全新的,但大量編譯器特色仍然需要了解。與以前版本的顯著區(qū)別是編譯器的安全檢查當(dāng)前默認(rèn)情況下是打開(kāi)的,讓我們來(lái)看一下編譯器的特點(diǎn)及在某些情況下它們是如何阻止在某些情況下受到攻擊。
Visual C++編譯器很久以前就開(kāi)始提供嚴(yán)格的運(yùn)行時(shí)安全檢查選項(xiàng),包括棧校驗(yàn),下溢和上溢檢查以及未初始化變量的識(shí)別。這些運(yùn)行時(shí)檢查由編譯器的/RTC選項(xiàng)來(lái)控制。雖然在早期的發(fā)展中捕獲錯(cuò)誤非常有用,但是對(duì)于發(fā)布版本性能上的損失卻是不能接受的。微軟的Visual C++.NET引入了/GS編譯開(kāi)關(guān),對(duì)于發(fā)行版本來(lái)說(shuō)它添加了有限的運(yùn)行時(shí)安全檢查。/GS開(kāi)關(guān)在編譯開(kāi)關(guān)中插入代碼,通過(guò)檢測(cè)函數(shù)的棧數(shù)據(jù)來(lái)檢測(cè)通?;跅5木彌_溢出。如果發(fā)現(xiàn)問(wèn)題,應(yīng)用程序?qū)⒈唤K止。為了減少運(yùn)行時(shí)檢查對(duì)性能的影響,編譯器辨別哪個(gè)函數(shù)易于攻擊并且僅針對(duì)這些函數(shù)來(lái)進(jìn)行安全檢查。安全檢查涉及到在函數(shù)的棧框架上增加一個(gè)cookie,在緩沖溢出的情況下它將被重寫(xiě)。函數(shù)指令的前后都添加了匯編指令。在函數(shù)執(zhí)行以前,源自cookie模塊的函數(shù)cookie先執(zhí)行計(jì)算。當(dāng)函數(shù)結(jié)束但在??臻g被收回前,cookie的??截惐粰z索以判斷它是否被更改。如果cookie未被更改,函數(shù)結(jié)束并繼續(xù)執(zhí)行程序的下一步,如果cookie被更改了,一個(gè)安全錯(cuò)誤句柄將被調(diào)用,它將結(jié)束應(yīng)用程序。
為了在Visual C++ 2005發(fā)布版本中控制這些編譯選項(xiàng),打開(kāi)工程的屬性頁(yè),單擊C/C++標(biāo)簽,在代碼發(fā)生屬性頁(yè)中,你將發(fā)現(xiàn)兩個(gè)屬性對(duì)應(yīng)于我剛剛描述的特點(diǎn)。Basic Runtime Checks屬性對(duì)應(yīng)于開(kāi)發(fā)時(shí)/RTC編譯選項(xiàng),在編譯版本中應(yīng)設(shè)置為BOTH。Buffer Security Check屬性相當(dāng)于編譯器的/GS選項(xiàng),對(duì)于發(fā)布版本應(yīng)設(shè)置為YES。
對(duì)于使用Visual C++ 2005的開(kāi)發(fā)人員來(lái)說(shuō),這些編譯特點(diǎn)在默認(rèn)情況下打開(kāi),這意味著你可以確信編譯器正在盡其可能阻止你代碼中的漏洞。然而,這并不意味著我們可以完全不關(guān)心安全問(wèn)題。開(kāi)發(fā)人員需要繼續(xù)為正確的代碼而努力,并且要考慮各種不同的、可能發(fā)生的安全威脅。編譯器僅僅可以阻止部分類(lèi)型的錯(cuò)誤發(fā)生。
要牢記這些編譯器提供的特殊的安全檢查僅適用于本地代碼,幸運(yùn)的是,托管代碼很少犯此類(lèi)的錯(cuò)誤。這里甚至于有更好的消息,Visual C++ 2005引進(jìn)了C++/CLI設(shè)計(jì)語(yǔ)言,它提供了.NET框架下最強(qiáng)有力的開(kāi)發(fā)語(yǔ)言。
六、新的C++編程語(yǔ)言
Visual C++ 2005發(fā)布版本提供了C++/CLI設(shè)計(jì)語(yǔ)言的一流的實(shí)現(xiàn)。C++/CLI是為.NET設(shè)計(jì)的系統(tǒng)編程語(yǔ)言。相對(duì)于其他語(yǔ)言來(lái)說(shuō),它在創(chuàng)建和使用.NET模塊和匯編上有更多的控制。C++/CLI對(duì)于C++開(kāi)發(fā)人員來(lái)說(shuō)更精細(xì)和自然,無(wú)論你是否熟悉C++或.NET框架,你將發(fā)現(xiàn)使用C++書(shū)寫(xiě)托管代碼是對(duì)ANSI C++自然文雅的擴(kuò)展,學(xué)習(xí)起來(lái)非常容易。
對(duì)于開(kāi)發(fā)應(yīng)用程序來(lái)說(shuō),有許多強(qiáng)制性的原因讓你來(lái)選擇托管代碼而不是本地C++。兩個(gè)最重要的原因是安全性和效率。通用運(yùn)行時(shí)語(yǔ)言(CLR)給你的代碼提供了一個(gè)安全的運(yùn)行環(huán)境。作為一個(gè)程序開(kāi)發(fā)人員,你不需要關(guān)心緩沖區(qū)溢出及因?yàn)槟阍谑褂们拔闯跏蓟兞康葐?wèn)題。安全問(wèn)題沒(méi)有完全消失,但是使用托管可以避免通常發(fā)生的一些錯(cuò)誤。
另外一個(gè)使用托管的原因是.NET框架下豐富的類(lèi)庫(kù)。雖然標(biāo)準(zhǔn)C++庫(kù)更適合于C++類(lèi)型編程,但是.NET框架包含了一個(gè)功能強(qiáng)大的函數(shù)庫(kù),這是標(biāo)準(zhǔn)C++庫(kù)所無(wú)法比擬的。.NET框架包括很多有用的集合類(lèi)、一個(gè)強(qiáng)大的數(shù)據(jù)操作庫(kù)、執(zhí)行很多流行的通訊協(xié)議的類(lèi),從SOCKETS到HTTP和網(wǎng)絡(luò)服務(wù)等等。雖然本地C++程序開(kāi)發(fā)人員可以以各種形似使用這些服務(wù),但通過(guò)使用.NET框架獲取的生產(chǎn)力主要因?yàn)樗慕y(tǒng)一性和連貫性。無(wú)論你是用System::Net::Sockets還是用System::Web名字空間,你將面對(duì)同樣的類(lèi)型,描述廣泛應(yīng)用的概念,例如流和字符串。這是.NET框架具有開(kāi)發(fā)高效率的最主要的原因。這讓程序人員更快速地書(shū)寫(xiě)更強(qiáng)有力的應(yīng)用程序,同時(shí)代碼更可靠。
Visual C++ 2005自然地準(zhǔn)許你在一個(gè)工程中混合本地與托管代碼,你可以繼續(xù)使用已經(jīng)存在的本地函數(shù)及類(lèi)的同時(shí),開(kāi)始使用越來(lái)越多的.NET框架下的類(lèi)庫(kù),甚至是寫(xiě)你的托管類(lèi)型。你可以將你的托管類(lèi)型定義為一個(gè)引用類(lèi)型或一個(gè)值類(lèi)型,雖然Visual C++編譯器允許你為了方面選擇使用棧語(yǔ)法或是為了控制管理資源使用通常的作用域規(guī)則,但值類(lèi)型在棧上而引用類(lèi)型位于CLR的托管堆上。
通過(guò)在你定義的class 和 struct 前添加ref來(lái)形成一個(gè)關(guān)鍵詞,定義了一個(gè)引用類(lèi)型。獲取和釋放資源按通常的方式完成,通過(guò)使用構(gòu)造和析構(gòu)函數(shù),正如這里說(shuō)明的:
編譯器負(fù)責(zé)Connection引用類(lèi)型的IDisposable接口的實(shí)現(xiàn),所以使用類(lèi)似C#、Visual Basic.NET的開(kāi)發(fā)人員可以使用任何對(duì)他們可用的資源管理結(jié)構(gòu)。對(duì)于C++開(kāi)發(fā)人員,有著與以前一樣的選擇。為了簡(jiǎn)化資源管理,并書(shū)寫(xiě)異常安全代碼,你可以簡(jiǎn)單地在棧上聲明一個(gè)Connection對(duì)象。當(dāng)一個(gè)對(duì)象超過(guò)其作用范圍后,執(zhí)行Dispose方法的析構(gòu)函數(shù)將被調(diào)用。下面是一個(gè)例子:
這個(gè)例子中,通過(guò)在函數(shù)返回調(diào)用前調(diào)用析構(gòu)函數(shù)來(lái)關(guān)閉這個(gè)Connection,這正如你在C++希望的那樣。如果你希望自己控制對(duì)象的生命期,僅僅需要使用gcnew這個(gè)關(guān)鍵詞來(lái)獲取connection對(duì)象的句柄。這個(gè)指針可以看作通常的指針(不含有通常的缺陷),并且這個(gè)對(duì)象的析構(gòu)函數(shù)可以簡(jiǎn)單地通過(guò)delete操作來(lái)調(diào)用。這個(gè)例子代碼如下 :
正如你所看到的,從本地C++到托管代碼,Visual C++ 2005帶來(lái)了簡(jiǎn)單靈活的資源管理方式,可以書(shū)寫(xiě)強(qiáng)壯的資源管理代碼對(duì)于書(shū)寫(xiě)正確、安全的代碼是非常重要的。
七、小結(jié)
無(wú)論是對(duì)于一個(gè)小的程序還是一個(gè)大的應(yīng)用,Visual C++ 2005發(fā)布版本都是一個(gè)功能強(qiáng)大的開(kāi)發(fā)工具,C運(yùn)行時(shí)庫(kù)和C++標(biāo)準(zhǔn)庫(kù)提供了一個(gè)強(qiáng)大的工具集,來(lái)發(fā)布功能強(qiáng)大的、強(qiáng)壯的本地應(yīng)用程序,同時(shí),對(duì)用C++書(shū)寫(xiě)托管代碼有著一流的支持,Visual C++ 2005在微軟的Windows開(kāi)發(fā)平臺(tái)上是獨(dú)一無(wú)二的強(qiáng)大的開(kāi)發(fā)工具。
from: http://www.97t.org/ArticleView/2005-9-4/Article_View_241.Htm
微軟的Visual C++2005發(fā)布版本對(duì)于有志于輕松、迅速地編寫(xiě)安全可靠的應(yīng)用程序的編程愛(ài)好者來(lái)說(shuō)是正確地選擇。正如你所聽(tīng)到的那樣,Visual C++中語(yǔ)言和庫(kù)的新特點(diǎn)使開(kāi)發(fā)安全、可靠的應(yīng)用程序比以前更容易。它即提供了功能強(qiáng)大并且靈活的標(biāo)準(zhǔn)C++,又提供了適于.NET框架下編程的最強(qiáng)大的開(kāi)發(fā)語(yǔ)言。
本文中,我主要探討Visual C++2005發(fā)布版本中部分語(yǔ)言和庫(kù)的新特色,無(wú)論是對(duì)于教學(xué)項(xiàng)目還是大的應(yīng)用工程,這都將幫助你在編寫(xiě)安全可靠的代碼時(shí)提高工作效率。
二、C運(yùn)行時(shí)庫(kù)的安全特點(diǎn)
如果你正在使用Visual C++創(chuàng)建使用C運(yùn)行時(shí)庫(kù)的應(yīng)用程序,你將非常欣慰地了解到現(xiàn)在你所依賴(lài)的許多庫(kù)函數(shù)都有了更安全的版本。對(duì)于需要一個(gè)或多個(gè)緩沖作為輸入的函數(shù)來(lái)說(shuō),已經(jīng)添加了長(zhǎng)度參數(shù),以此讓函數(shù)來(lái)確信不會(huì)超越緩沖的邊界?,F(xiàn)在更多的函數(shù)開(kāi)始對(duì)參數(shù)進(jìn)行合法性檢查,必要時(shí)將調(diào)用無(wú)效參數(shù)處理器。讓我們來(lái)看一些簡(jiǎn)單的例子:
C運(yùn)行時(shí)庫(kù)中最不可靠的是gets函數(shù),它從標(biāo)準(zhǔn)輸入中讀取一行。思考下面的一個(gè)簡(jiǎn)單的例子:
char buffer[10] = { 0 }; gets(buffer); |
第一行代碼聲明了一個(gè)緩沖變量,并將緩沖區(qū)中的字符初始化設(shè)置為0。為避免意外情況發(fā)生將變量初始化為一個(gè)眾所周知的值是一個(gè)非常好的主意。緊接著,看似清白無(wú)辜的gets函數(shù)從標(biāo)準(zhǔn)的輸入流中讀取一行并且寫(xiě)入到buffer緩沖區(qū)內(nèi)。這有什么錯(cuò)誤嗎?對(duì)于函數(shù)來(lái)說(shuō)C類(lèi)型的數(shù)組不能實(shí)現(xiàn)值傳遞,而是傳遞了指向數(shù)組第一個(gè)元素的指針。所以在函數(shù)看來(lái),char[ ]相當(dāng)于char*指針,并且是一個(gè)不附帶可以決定所指向的緩沖區(qū)大小尺寸的任何額外信息的原始指針。那么gets函數(shù)是怎么作的呢?它假設(shè)緩沖區(qū)無(wú)限大(UINT_MAX 是有精確尺寸的),并將持續(xù)地從輸入流中拷貝字符到緩沖區(qū)內(nèi)。攻擊者可以輕易地使用這個(gè)弱點(diǎn),這種不廣為人知的類(lèi)型錯(cuò)誤被稱(chēng)為緩沖溢出。
很多最初的C運(yùn)行時(shí)庫(kù)函數(shù)遭受同樣的與參數(shù)確認(rèn)有關(guān)的問(wèn)題,并且現(xiàn)在因此受到抨擊。一定要牢記對(duì)于當(dāng)前所要寫(xiě)的應(yīng)用程序來(lái)說(shuō),性能處于次要地位,我們現(xiàn)在生活在一個(gè)安全第一的世界。每一個(gè)受到批評(píng)的函數(shù)已經(jīng)被一個(gè)提供同樣函數(shù)功能,但添加了安全特點(diǎn)的函數(shù)所代替。當(dāng)然,根據(jù)你在已經(jīng)存在的代碼中所使用的舊的庫(kù)函數(shù)的多少,你可能需要花費(fèi)一些時(shí)間來(lái)代碼更替到新的、更安全的版本。這些新的函數(shù)有一個(gè)_s后綴,例如,gets函數(shù)被gets_s函數(shù)代替;遭受抨擊的strcpy函數(shù)被strcpy_s函數(shù)代替。這里有一個(gè)例子:
char buffer[10] = { 0 }; gets_s(buffer, sizeof (buffer) / sizeof (buffer[0])); |
gets_s函數(shù)有一個(gè)額外的參數(shù),用來(lái)顯示可以寫(xiě)入的最大字符數(shù)量,這里包括一個(gè)NULL終結(jié)符。我使用了sizeof操作符,它能決定數(shù)組的長(zhǎng)度,因?yàn)榫幾g器在編譯時(shí)決定sizeof操作符返回的結(jié)果。記住,sizeof返回操作數(shù)的長(zhǎng)度是以字節(jié)為單位的,所以用數(shù)組長(zhǎng)度來(lái)除以數(shù)組中第一個(gè)元素的長(zhǎng)度將返回?cái)?shù)組中元素的個(gè)數(shù)。這種簡(jiǎn)單的方法可以移植到Unicode編碼下使用_getws_s的情況,這個(gè)函數(shù)也需要得知以字節(jié)為單位的緩沖區(qū)長(zhǎng)度。
正如我所提到的,另外一個(gè)接受安全檢查的常用函數(shù)strcpy函數(shù),就象gets函數(shù)一樣,它沒(méi)有方法來(lái)保證有效的緩沖區(qū)尺寸,所以它只能假定緩沖足夠大來(lái)容納要拷貝的字符串。在程序運(yùn)行時(shí),這將導(dǎo)致不可預(yù)料的行為,正如我所提到的,為了安全需要避免這些不可預(yù)料的行為,這有一個(gè)使用安全的strcpy_s函數(shù)的例子。
char source[] = Hello world!; char destination[20] = { 0 }; strcpy_s(destination, sizeof (destination) / sizeof (destination[0]), source); |
有很多原因來(lái)喜歡這個(gè)新的strcpy_s函數(shù)。最明顯的區(qū)別是的額外的、以字節(jié)為單位的參數(shù),它用來(lái)確認(rèn)緩沖區(qū)大小。這允許strcpy_s函數(shù)可以進(jìn)行運(yùn)行時(shí)檢查,以確定寫(xiě)入的字符沒(méi)有超過(guò)目標(biāo)緩沖區(qū)的邊界。還有一些其它的檢查方法來(lái)確定參數(shù)的有效性。在調(diào)試版本中這些檢測(cè)方法,包括顯示調(diào)試報(bào)告的斷言(assertions)方法,如果它們的條件沒(méi)有滿(mǎn)足,它們將顯示調(diào)試報(bào)告。無(wú)論是調(diào)試還是發(fā)行版本,如果一個(gè)特定的條件沒(méi)有得到滿(mǎn)足,一個(gè)無(wú)效的參數(shù)管理器將被調(diào)用,它默認(rèn)的行為是拋出一個(gè)訪(fǎng)問(wèn)沖突來(lái)終止應(yīng)用程序。這非常好的實(shí)現(xiàn)了讓你的應(yīng)用程序持續(xù)運(yùn)行,而不會(huì)產(chǎn)生不可預(yù)期的結(jié)果。當(dāng)然,這種情況完全可以通過(guò)確保類(lèi)似于strcpy_s的函數(shù)不調(diào)用無(wú)效參數(shù)來(lái)避免。
前一個(gè)例子可以通過(guò)新的_countof宏來(lái)進(jìn)一步簡(jiǎn)化,這個(gè)宏移拋開(kāi)了對(duì)有錯(cuò)誤傾向的sizeof操作符的需要。_countof宏返回C類(lèi)型數(shù)組的元素?cái)?shù)量。這個(gè)宏本身對(duì)應(yīng)了一個(gè)模版,如果傳遞一個(gè)原始指針的話(huà),它將無(wú)法通過(guò)編譯。這有一個(gè)例子:
strcpy_s(destination, _countof(destination), source); |
三、使用C++標(biāo)準(zhǔn)庫(kù)
已經(jīng)看了C運(yùn)行時(shí)庫(kù)新增強(qiáng)的安全特性,讓我們來(lái)看一下如何使用C++標(biāo)準(zhǔn)庫(kù)來(lái)進(jìn)一步減少你的代碼中的相似錯(cuò)誤。
當(dāng)你從C運(yùn)行時(shí)庫(kù)轉(zhuǎn)向C++的標(biāo)準(zhǔn)庫(kù),讓你從C++開(kāi)始受益的一個(gè)最有效的方法是使用庫(kù)中的矢量類(lèi)(Vector class)。矢量類(lèi)是C++標(biāo)準(zhǔn)庫(kù)中的一個(gè)模仿一維T數(shù)組的容器類(lèi),這里T可以是事實(shí)上的任何類(lèi)型,你的代碼中使用緩沖區(qū)的地方都可以用矢量對(duì)象來(lái)代替。讓我們來(lái)考慮上一節(jié)的例子,第一個(gè)例子我們使用gets_s函數(shù)來(lái)從標(biāo)準(zhǔn)輸入中讀取一個(gè)行,考慮用下面的代碼代替:
std::vector<char> buffer(10); gets_s(&buffer[0], buffer.size()); |
最值得注意的一個(gè)區(qū)別是緩沖區(qū)變量現(xiàn)在是一個(gè)帶有可用方法和操作符的矢量對(duì)象,這個(gè)矢量對(duì)象初始化為10個(gè)字節(jié)長(zhǎng)度,并且構(gòu)造函數(shù)將每個(gè)元素都初始化為0,表達(dá)式&buffer[0]用于得到矢量對(duì)象的第一個(gè)元素的地址,向期待一個(gè)簡(jiǎn)單緩沖區(qū)的C函數(shù)傳遞一個(gè)矢量對(duì)象是一種正確的方法。與sizeof操作符不同的是,所有的容器的尺寸測(cè)量是基于元素的,而不是基與字節(jié)的。例如,矢量的size方法返回的是容器的元素?cái)?shù)量。
在上節(jié)的第二個(gè)例子里,我們使用strcpy_s函數(shù)從源緩沖區(qū)向目標(biāo)緩沖區(qū)拷貝字符。應(yīng)該清楚矢量對(duì)象是如何代替原始的C類(lèi)型的數(shù)組,為了更好的說(shuō)明這一點(diǎn),讓我們來(lái)考慮另外一個(gè)非常有用的C++標(biāo)準(zhǔn)庫(kù)的容器。
提供的basic_string類(lèi)使得字符串在C++中可以作為正常的類(lèi)型來(lái)操作。它提供了各種各樣的重載操作符,為C++程序開(kāi)發(fā)人員提供了自然的編程模式。由于優(yōu)于strcopy_s及其它操作字符串的函數(shù),你應(yīng)該首選basic_string函數(shù)。basic_string以字節(jié)為單位的T類(lèi)型容器。這里T是字符類(lèi)型。C++標(biāo)準(zhǔn)類(lèi)庫(kù)對(duì)于常用的字符類(lèi)型提供類(lèi)型定義。string和wstring中的元素類(lèi)型分別被定義為char和wchar類(lèi)型。下面的例子說(shuō)明basic_string類(lèi)是多么簡(jiǎn)單和安全:
std::string source = Hello world!; std::string destination = source; |
basic_string類(lèi)也提供了你所希望的、常用的字符串操作的方法和操作符,象字符串聯(lián)合及子串的搜索。
最后,C++標(biāo)準(zhǔn)庫(kù)提供了一個(gè)功能非常強(qiáng)大的I/O庫(kù),用來(lái)安全、簡(jiǎn)單地與標(biāo)準(zhǔn)輸入輸出、文件流進(jìn)行交互操作。雖然對(duì)于gets_s函數(shù)來(lái)說(shuō)使用矢量對(duì)象比使用C類(lèi)型的數(shù)組更好,但你可以通過(guò)使用定義的basic_istream 和 basic_ostream類(lèi)進(jìn)一步簡(jiǎn)化。實(shí)際上,你可以書(shū)寫(xiě)簡(jiǎn)單并且類(lèi)型安全的代碼從流中來(lái)讀取包括字符串在內(nèi)的任何類(lèi)型。
std::string word; int number = 0; std::cin >> word >> number; std::cout << word << std::endl << number << std::endl; |
cin被定義成一個(gè)basic_istream流,從標(biāo)準(zhǔn)的輸入中提取字符類(lèi)型的元素。wcin是用于wchar_t元素。另一方面,cout被定義為一個(gè)basic_ostream流,用于向標(biāo)準(zhǔn)的輸出流寫(xiě)入操作。正如你能想象的,這種模式比起gets_s和puts函數(shù)來(lái)可以無(wú)限的擴(kuò)展。但是,真正的價(jià)值是在于它非常難以產(chǎn)生讓你的應(yīng)用程序出現(xiàn)安全裂痕的錯(cuò)誤。
四、C++標(biāo)準(zhǔn)庫(kù)中的邊界檢查
默認(rèn)情況,C++標(biāo)準(zhǔn)庫(kù)中大量的容器對(duì)象和迭代對(duì)象沒(méi)有提供邊界檢查。例如,矢量的下標(biāo)操作符通常是一個(gè)比較快,但有潛在的危險(xiǎn)性的操作單獨(dú)元素的方法。如果你正在尋找得到確認(rèn)檢查的操作方法,你可以轉(zhuǎn)向at方法。安全性的增加是以犧牲性能為代價(jià)的。當(dāng)然,絕大情況下性能的降低是可以忽略不計(jì)的,但是對(duì)于性能要求第一位的代碼來(lái)說(shuō),這可能是非常有害的,思考一下下面的簡(jiǎn)單函數(shù):
void PrintAll(const std::vector<int>& numbers) { for (size_t index = 0; index < numbers.size(); ++index) { std::cout << numbers[index] << std::endl; } } void PrintN(const std::vector<int>& numbers, size_t index) { std::cout << numbers.at(index) << std::endl; } |
PrintAll函數(shù)使用了下標(biāo)操作符,因?yàn)樗饕珊瘮?shù)控制,并且可以確認(rèn)是安全的。另一方面,PrintN函數(shù)不能保證索引的有效性,所以它使用了更安全的at方法來(lái)代替。當(dāng)然,并不是所有的容器的存取操作都象這么簡(jiǎn)潔明了。
在保證C++標(biāo)準(zhǔn)庫(kù)的安全特性的同時(shí),Visual C++2005繼續(xù)堅(jiān)持并在很多情況下改進(jìn)了C++標(biāo)準(zhǔn)庫(kù)的運(yùn)行特性,同時(shí)提供了調(diào)節(jié)C++標(biāo)準(zhǔn)庫(kù)安全性的特色。一項(xiàng)受人歡迎的改進(jìn)是在調(diào)試版本中添加了范圍檢查,這對(duì)你的發(fā)行版本性能并不構(gòu)成影響。但這確實(shí)幫助你在調(diào)試階段捕獲越界錯(cuò)誤,甚至是使用傳統(tǒng)上不安全的下標(biāo)操作符的代碼。
不安全的函數(shù),象vector的下標(biāo)操作算子,和其他的函數(shù),象它的front函數(shù),如果不恰當(dāng)?shù)恼{(diào)用,通常會(huì)導(dǎo)致不明確的行為。如果你幸運(yùn)的話(huà),它將很快導(dǎo)致一個(gè)存取沖突,這將使你的應(yīng)用程序崩潰。如果你不那么走運(yùn)的話(huà),它可能默默地持續(xù)運(yùn)轉(zhuǎn)并導(dǎo)致不可預(yù)知的副效應(yīng),這將破壞數(shù)據(jù)并可能被進(jìn)攻者利用。為了保護(hù)你的發(fā)行版本的應(yīng)用程序,Visual C++2005引入了_SECURE_SCL符號(hào),用來(lái)給那些非安全的函數(shù)添加運(yùn)行時(shí)檢查。象下面的代碼那樣在你的應(yīng)用程序中簡(jiǎn)單地定義這個(gè)符號(hào)可以添加額外的實(shí)時(shí)檢查并阻止不確切的行為。
#define _SECURE_SCL 1 |
緊記定義這個(gè)符號(hào)對(duì)你的程序沖擊很大,大量的合法的,但是具有潛在非安全的操作將在編譯時(shí)將無(wú)法通過(guò),以避免在運(yùn)行時(shí)出現(xiàn)潛在BUG。思考下面的使用Copy運(yùn)算的例子:
std::copy(first, last, destination); |
其中,first和last是定義拷貝范圍的迭代參數(shù),destination是輸出迭代參數(shù),指示了目標(biāo)緩沖區(qū)的位置,這個(gè)位置用來(lái)拷貝范圍之內(nèi)的第一個(gè)元素。這里有一個(gè)危險(xiǎn)是destination所對(duì)應(yīng)的目標(biāo)緩沖區(qū)或容器不足夠大,無(wú)法容納所要拷貝的元素。如果Destination是一個(gè)需要安全檢查的迭代參數(shù),類(lèi)似的錯(cuò)誤將被捕獲。但是,這僅僅是一個(gè)假設(shè)。如果destination是一個(gè)簡(jiǎn)單的指針,將無(wú)法保證copy運(yùn)算函數(shù)正確運(yùn)轉(zhuǎn)。這時(shí)當(dāng)然會(huì)想到_SECURE_SCL符號(hào)來(lái)避免這一問(wèn)題,這種情況下,代碼甚至是不能編譯,以此避免任何可能的運(yùn)行時(shí)錯(cuò)誤。就象你想象的那樣,這將需要重寫(xiě)更完美有效的代碼。所以,這是一個(gè)更好的理由支持C++標(biāo)準(zhǔn)庫(kù)容器,避免使用C類(lèi)型數(shù)組。
五、編譯器的安全特點(diǎn)
雖然對(duì)于Visual C++2005來(lái)說(shuō)并不是全新的,但大量編譯器特色仍然需要了解。與以前版本的顯著區(qū)別是編譯器的安全檢查當(dāng)前默認(rèn)情況下是打開(kāi)的,讓我們來(lái)看一下編譯器的特點(diǎn)及在某些情況下它們是如何阻止在某些情況下受到攻擊。
Visual C++編譯器很久以前就開(kāi)始提供嚴(yán)格的運(yùn)行時(shí)安全檢查選項(xiàng),包括棧校驗(yàn),下溢和上溢檢查以及未初始化變量的識(shí)別。這些運(yùn)行時(shí)檢查由編譯器的/RTC選項(xiàng)來(lái)控制。雖然在早期的發(fā)展中捕獲錯(cuò)誤非常有用,但是對(duì)于發(fā)布版本性能上的損失卻是不能接受的。微軟的Visual C++.NET引入了/GS編譯開(kāi)關(guān),對(duì)于發(fā)行版本來(lái)說(shuō)它添加了有限的運(yùn)行時(shí)安全檢查。/GS開(kāi)關(guān)在編譯開(kāi)關(guān)中插入代碼,通過(guò)檢測(cè)函數(shù)的棧數(shù)據(jù)來(lái)檢測(cè)通?;跅5木彌_溢出。如果發(fā)現(xiàn)問(wèn)題,應(yīng)用程序?qū)⒈唤K止。為了減少運(yùn)行時(shí)檢查對(duì)性能的影響,編譯器辨別哪個(gè)函數(shù)易于攻擊并且僅針對(duì)這些函數(shù)來(lái)進(jìn)行安全檢查。安全檢查涉及到在函數(shù)的棧框架上增加一個(gè)cookie,在緩沖溢出的情況下它將被重寫(xiě)。函數(shù)指令的前后都添加了匯編指令。在函數(shù)執(zhí)行以前,源自cookie模塊的函數(shù)cookie先執(zhí)行計(jì)算。當(dāng)函數(shù)結(jié)束但在??臻g被收回前,cookie的??截惐粰z索以判斷它是否被更改。如果cookie未被更改,函數(shù)結(jié)束并繼續(xù)執(zhí)行程序的下一步,如果cookie被更改了,一個(gè)安全錯(cuò)誤句柄將被調(diào)用,它將結(jié)束應(yīng)用程序。
為了在Visual C++ 2005發(fā)布版本中控制這些編譯選項(xiàng),打開(kāi)工程的屬性頁(yè),單擊C/C++標(biāo)簽,在代碼發(fā)生屬性頁(yè)中,你將發(fā)現(xiàn)兩個(gè)屬性對(duì)應(yīng)于我剛剛描述的特點(diǎn)。Basic Runtime Checks屬性對(duì)應(yīng)于開(kāi)發(fā)時(shí)/RTC編譯選項(xiàng),在編譯版本中應(yīng)設(shè)置為BOTH。Buffer Security Check屬性相當(dāng)于編譯器的/GS選項(xiàng),對(duì)于發(fā)布版本應(yīng)設(shè)置為YES。
對(duì)于使用Visual C++ 2005的開(kāi)發(fā)人員來(lái)說(shuō),這些編譯特點(diǎn)在默認(rèn)情況下打開(kāi),這意味著你可以確信編譯器正在盡其可能阻止你代碼中的漏洞。然而,這并不意味著我們可以完全不關(guān)心安全問(wèn)題。開(kāi)發(fā)人員需要繼續(xù)為正確的代碼而努力,并且要考慮各種不同的、可能發(fā)生的安全威脅。編譯器僅僅可以阻止部分類(lèi)型的錯(cuò)誤發(fā)生。
要牢記這些編譯器提供的特殊的安全檢查僅適用于本地代碼,幸運(yùn)的是,托管代碼很少犯此類(lèi)的錯(cuò)誤。這里甚至于有更好的消息,Visual C++ 2005引進(jìn)了C++/CLI設(shè)計(jì)語(yǔ)言,它提供了.NET框架下最強(qiáng)有力的開(kāi)發(fā)語(yǔ)言。
六、新的C++編程語(yǔ)言
Visual C++ 2005發(fā)布版本提供了C++/CLI設(shè)計(jì)語(yǔ)言的一流的實(shí)現(xiàn)。C++/CLI是為.NET設(shè)計(jì)的系統(tǒng)編程語(yǔ)言。相對(duì)于其他語(yǔ)言來(lái)說(shuō),它在創(chuàng)建和使用.NET模塊和匯編上有更多的控制。C++/CLI對(duì)于C++開(kāi)發(fā)人員來(lái)說(shuō)更精細(xì)和自然,無(wú)論你是否熟悉C++或.NET框架,你將發(fā)現(xiàn)使用C++書(shū)寫(xiě)托管代碼是對(duì)ANSI C++自然文雅的擴(kuò)展,學(xué)習(xí)起來(lái)非常容易。
對(duì)于開(kāi)發(fā)應(yīng)用程序來(lái)說(shuō),有許多強(qiáng)制性的原因讓你來(lái)選擇托管代碼而不是本地C++。兩個(gè)最重要的原因是安全性和效率。通用運(yùn)行時(shí)語(yǔ)言(CLR)給你的代碼提供了一個(gè)安全的運(yùn)行環(huán)境。作為一個(gè)程序開(kāi)發(fā)人員,你不需要關(guān)心緩沖區(qū)溢出及因?yàn)槟阍谑褂们拔闯跏蓟兞康葐?wèn)題。安全問(wèn)題沒(méi)有完全消失,但是使用托管可以避免通常發(fā)生的一些錯(cuò)誤。
另外一個(gè)使用托管的原因是.NET框架下豐富的類(lèi)庫(kù)。雖然標(biāo)準(zhǔn)C++庫(kù)更適合于C++類(lèi)型編程,但是.NET框架包含了一個(gè)功能強(qiáng)大的函數(shù)庫(kù),這是標(biāo)準(zhǔn)C++庫(kù)所無(wú)法比擬的。.NET框架包括很多有用的集合類(lèi)、一個(gè)強(qiáng)大的數(shù)據(jù)操作庫(kù)、執(zhí)行很多流行的通訊協(xié)議的類(lèi),從SOCKETS到HTTP和網(wǎng)絡(luò)服務(wù)等等。雖然本地C++程序開(kāi)發(fā)人員可以以各種形似使用這些服務(wù),但通過(guò)使用.NET框架獲取的生產(chǎn)力主要因?yàn)樗慕y(tǒng)一性和連貫性。無(wú)論你是用System::Net::Sockets還是用System::Web名字空間,你將面對(duì)同樣的類(lèi)型,描述廣泛應(yīng)用的概念,例如流和字符串。這是.NET框架具有開(kāi)發(fā)高效率的最主要的原因。這讓程序人員更快速地書(shū)寫(xiě)更強(qiáng)有力的應(yīng)用程序,同時(shí)代碼更可靠。
Visual C++ 2005自然地準(zhǔn)許你在一個(gè)工程中混合本地與托管代碼,你可以繼續(xù)使用已經(jīng)存在的本地函數(shù)及類(lèi)的同時(shí),開(kāi)始使用越來(lái)越多的.NET框架下的類(lèi)庫(kù),甚至是寫(xiě)你的托管類(lèi)型。你可以將你的托管類(lèi)型定義為一個(gè)引用類(lèi)型或一個(gè)值類(lèi)型,雖然Visual C++編譯器允許你為了方面選擇使用棧語(yǔ)法或是為了控制管理資源使用通常的作用域規(guī)則,但值類(lèi)型在棧上而引用類(lèi)型位于CLR的托管堆上。
通過(guò)在你定義的class 和 struct 前添加ref來(lái)形成一個(gè)關(guān)鍵詞,定義了一個(gè)引用類(lèi)型。獲取和釋放資源按通常的方式完成,通過(guò)使用構(gòu)造和析構(gòu)函數(shù),正如這里說(shuō)明的:
ref class Connection { public: Connection(String^ server) { Server = server; Console::WriteLine(Aquiring connection to server.); } ~Connection() { Console::WriteLine(Disconnecting from server.); } property String^ Server; }; |
編譯器負(fù)責(zé)Connection引用類(lèi)型的IDisposable接口的實(shí)現(xiàn),所以使用類(lèi)似C#、Visual Basic.NET的開(kāi)發(fā)人員可以使用任何對(duì)他們可用的資源管理結(jié)構(gòu)。對(duì)于C++開(kāi)發(fā)人員,有著與以前一樣的選擇。為了簡(jiǎn)化資源管理,并書(shū)寫(xiě)異常安全代碼,你可以簡(jiǎn)單地在棧上聲明一個(gè)Connection對(duì)象。當(dāng)一個(gè)對(duì)象超過(guò)其作用范圍后,執(zhí)行Dispose方法的析構(gòu)函數(shù)將被調(diào)用。下面是一個(gè)例子:
void UseStackConnection() { Connection connection(sample.kennyandkarin.com); Console::WriteLine(Connection to {0} established!, connection.Server); } |
這個(gè)例子中,通過(guò)在函數(shù)返回調(diào)用前調(diào)用析構(gòu)函數(shù)來(lái)關(guān)閉這個(gè)Connection,這正如你在C++希望的那樣。如果你希望自己控制對(duì)象的生命期,僅僅需要使用gcnew這個(gè)關(guān)鍵詞來(lái)獲取connection對(duì)象的句柄。這個(gè)指針可以看作通常的指針(不含有通常的缺陷),并且這個(gè)對(duì)象的析構(gòu)函數(shù)可以簡(jiǎn)單地通過(guò)delete操作來(lái)調(diào)用。這個(gè)例子代碼如下 :
void UseHeapConnection() { Connection^ connection = gcnew Connection(sample.kennyandkarin.com); try { Console::WriteLine(Connection to {0} established!, connection->Server); } finally { delete connection; } } |
正如你所看到的,從本地C++到托管代碼,Visual C++ 2005帶來(lái)了簡(jiǎn)單靈活的資源管理方式,可以書(shū)寫(xiě)強(qiáng)壯的資源管理代碼對(duì)于書(shū)寫(xiě)正確、安全的代碼是非常重要的。
七、小結(jié)
無(wú)論是對(duì)于一個(gè)小的程序還是一個(gè)大的應(yīng)用,Visual C++ 2005發(fā)布版本都是一個(gè)功能強(qiáng)大的開(kāi)發(fā)工具,C運(yùn)行時(shí)庫(kù)和C++標(biāo)準(zhǔn)庫(kù)提供了一個(gè)強(qiáng)大的工具集,來(lái)發(fā)布功能強(qiáng)大的、強(qiáng)壯的本地應(yīng)用程序,同時(shí),對(duì)用C++書(shū)寫(xiě)托管代碼有著一流的支持,Visual C++ 2005在微軟的Windows開(kāi)發(fā)平臺(tái)上是獨(dú)一無(wú)二的強(qiáng)大的開(kāi)發(fā)工具。
from: http://www.97t.org/ArticleView/2005-9-4/Article_View_241.Htm
posted on 2006-11-09 23:24 weidagang2046 閱讀(356) 評(píng)論(0) 編輯 收藏 所屬分類(lèi): C/C++