




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
C++程序設計第11章C++/CLI程序設計基礎第11章C++/CLI程序設計基礎VisualC++2010不僅能用ISO標準C++開發(fā)直接運行于Windows操作系統(tǒng)之上的應用程序,也能設計基于.NET平臺的應用程序。微軟公司對標準C++進行了擴展,專門為.NET平臺設計了C++/CLI,目的是使廣大VisualC++程序員可以用C++語言方便地創(chuàng)建運行于.NET框架之上的應用程序。本章重點介紹C++/CLI為.NET平臺上的程序設計對標準C++所做的擴展,主要內容有值類型、引用類型、裝箱與拆箱、托管數(shù)組、句柄、托管類、接口與多態(tài)、枚舉、異常、模板與泛型、委托與事件等C++/CLI基礎知識和.NET托管代碼編程技術。11.1概述
11.2C++/CLI的基本數(shù)據(jù)類型
11.3C++/CLI的句柄、裝箱與拆箱
11.4C++/CLI的字符串和數(shù)組
11.5C++/CLI中的類和屬性
第11章C++/CLI程序設計基礎11.6C++/CLI中的多態(tài)與接口
11.7C++/CLI中的模板和泛型
11.9C++/CLI中的枚舉
11.8C++/CLI中的異常
11.10.NET中的委托與事件11.1概述.NET平臺是微軟公司為簡化在第三代互聯(lián)網的分布式環(huán)境下應用程序的開發(fā),基于開放互聯(lián)網標準和協(xié)議之上,實現(xiàn)異質語言和平臺高度交互性而創(chuàng)建的新一代計算和通信平臺。.NET平臺主要由5個部分構成:Windows.NET、.NET企業(yè)級服務器、.NETWeb服務構件、.NET框架和VisualStudio.NET。
(1)Windows.NET是可以運行.NET程序的操作系統(tǒng)的統(tǒng)稱,主要包括WindowsXP、WindowsServer2003、Windows7等操作系統(tǒng)和各種應用服務軟件。(2).NET企業(yè)級服務器是微軟公司推出的進行企業(yè)集成和管理的所有基于Web的各種服務器應用的系列產品,包括ApplicationCenter2000、SQLServer2008、BizTalkServer2000等。
(3).NETWeb服務構件是保證.NET正常運行的公用性Web服務組件。(4).NET框架是.NET的核心部分,是支持生成和運行下一代應用程序和Web服務的內部Windows組件。.NET框架的關鍵組件為公共語言運行時(CommonLanguageRuntime,CLR)和.NET基礎類庫(BasicClassLibrary,BCL),BCL中包括了大量用于支持ADO.NET、ASP.NET、Windows窗體和WindowsPresentationFoundation應用開發(fā)的類。(5)VisualStudio.NET是用于建立.NET框架應用程序而推出的應用軟件開發(fā)工具,其中包含C#.NET、C++.NET、VB.NET和J#等開發(fā)環(huán)境,支持多種程序設計語言的單獨和混合方式的軟件開發(fā)。11.1概述11.1概述
.NET平臺的整體環(huán)境結構如圖11-1所示。.NET框架提供了托管執(zhí)行環(huán)境、簡化的開發(fā)和部署以及與各種編程語言的集成。
公共語言運行時是.NET框架的基礎,可以將其看作是一個在執(zhí)行時管理代碼的代理,它提供內存管理、線程管理和遠程處理等核心服務,并且還強制實施嚴格的類型安全以及可提高安全性和可靠性的其他形式的代碼準確性。事實上,代碼管理的概念是運行時的基本原則。以CLR為目標的代碼稱為托管代碼,而不以CLR為目標的代碼稱為非托管代碼。.NET框架的另一個主要組件是類庫,它是一個綜合性的面向對象的可重用類型集合,可以使用它開發(fā)多種應用程序,這些應用程序包括傳統(tǒng)的命令行或圖形用戶界面(GraphicalUserInterface)應用程序,也包括基于ASP.NET的網絡服務應用程序,如Web窗體和XMLWeb服務。11.1概述11.1概述.NET框架可由非托管組件承載,這些組件將公共語言運行時加載到它們的進程中并啟動托管代碼的執(zhí)行,從而創(chuàng)建一個可以同時利用托管和非托管功能的軟件環(huán)境。公共語言運行時為多種高級語言提供了標準化的運行環(huán)境,在VisualStudio.NET中能用于開發(fā)的語言就有VisualBasic、C#、C++和J#。CRL的規(guī)范由公共語言基礎結構(CommonLanguageInfrastructure,CLI)描述,其中包括了數(shù)據(jù)類型、對象存儲等與程序設計語言相關的設計規(guī)范。CLI的標準化工作由歐洲計算機制造商協(xié)會(EuropeanComputerManufacturersAssociation,ECMA)完成并成為ISO標準,它們分別是EMCA-335和ISO/IEC23271。11.1概述本質上,CLI提供了一套可執(zhí)行代碼和它運行所需要的虛擬執(zhí)行環(huán)境的規(guī)范,虛擬機運行環(huán)境能使各種高級語言設計的應用軟件不修改源代碼即能在不同的操作系統(tǒng)上運行。CLR是微軟對CLI的一個實現(xiàn),也是目前最好的實現(xiàn),另一個實例是Novell公司的一個開放源代碼的項目Mono。CLI主要包括通用類型系統(tǒng)(CommonTypeSystem,CTS)、元數(shù)據(jù)(Metadata)、公共語言規(guī)范(CommonLanguageSpecification,CLS)、通用中間語言(CommonIntermediateLanguage,CIL)和虛擬執(zhí)行系統(tǒng)(VirtualExecutionSystem,VES)幾個部分。通用類型系統(tǒng)是CLI的基礎,它是一個類型規(guī)范,定義了所有CLI平臺上可以定義的類型的集合,所有基于CLI的語言類型都是CTS的一個子集,目前C++/CLI是對CTS描述支持最好的高級語言。11.1概述元數(shù)據(jù)是描述其他數(shù)據(jù)的數(shù)據(jù)(DataAboutOtherData),或者說是用于提供某種資源的有關信息的結構數(shù)據(jù)(StructuredData)。在CLI中,用元數(shù)據(jù)描述和引用CTS定義的類型,元數(shù)據(jù)以一種獨立于任何語言的形式存儲,正是元數(shù)據(jù)賦予了組件自描述的能力。公共語言規(guī)范是用以確保所有CLI語言能夠互操作的一組規(guī)則,它定義了所有CLI語言都必須支持的一個最小功能子集。各CLI語言可以選擇自己對CTS的一部分的映射,但是為了確保不同語言的交互,至少應該支持CLS所定義的最小功能集。通用中間語言是一種中性語言,更準確地說,是一套與處理器無關的指令集合。任何.NET編程語言所編寫的程序均被編譯成通用中間語言指令集,程序運行時再通過JIT(Just-In-Time)實時編譯器映射為機器碼。
11.1概述虛擬執(zhí)行系統(tǒng)為CLI程序提供了一個在各種可能的平臺上加載和執(zhí)行托管代碼的虛擬機環(huán)境。它只是一個規(guī)范,.NET框架和Mono各有自己的實現(xiàn)。VisualC++2010開發(fā)平臺支持Windows下多種應用程序的設計,如控制臺應用程序、MFC本地窗體應用程序、.NET窗體應用程序、ActiveX控件等。11.2C++/CLI的基本數(shù)據(jù)類型ISO標準C++中的基本數(shù)據(jù)類型(如int、double、char和bool)在C++/CLI程序中可以繼續(xù)使用,但是它們已被編譯器映射到在System命名空間中定義的CLI值類類型(ValueClassType)。ISO標準C++基本類型名稱是CLI中相對應的值類類型的簡略形式。表11-1給出了基本數(shù)據(jù)類型和對應的值類型,以及為它們分配的內存大小。11.2C++/CLI的基本數(shù)據(jù)類型基本數(shù)據(jù)類型CLI值類型大小(字節(jié))boolSystem::Boolean1char、singedcharSystem::SByte1unsignedcharSystem::Byte1shortSystem::Int162unsignedshortSystem::UInt162int、longSystem::Int324unsignedint、unsignedlongSystem::UInt324longlongSystem::Int648unsignedlonglongSystem::UInt648floatSystem::Single4double、longdoubleSystem::Double8wchar_tSystem::Char211.2C++/CLI的基本數(shù)據(jù)類型在C++/CLI程序中,用基本數(shù)據(jù)類型定義變量與CLI的簡單值類型定義變量等價。例如:doublevalue=2.5;//等價于System::Doublevalue=2.5;在VisualC++2010中,創(chuàng)建CLR控制臺應用程序的方法與Win32控制臺應用程序創(chuàng)建的過程基本類似,主要不同點是在新建項目時選擇“CLR控制臺應用程序”模板。在項目創(chuàng)建過程中,系統(tǒng)自動為新建項目添加了主函數(shù),并且其中含有輸出“HelloWorld”字符串的輸出語句。【例11-1】設計CLR控制臺應用程序,輸出變量的類型信息。
11.2C++/CLI的基本數(shù)據(jù)類型程序說明:(1) 在CLR控制臺應用程序中,本地C++的標準控制臺輸出和輸入方法(cout和cin)已不能使用,取而代之的是用Console類進行標準輸入和輸出。Console類定義在System命名空間,其中標準輸出函數(shù)為WriteLine()和Write(),標準輸入函數(shù)為ReadLine()、Read()和ReadKey()。WriteLine(L“intx=100;類型:{0}\t值:{1}”,x.GetType(),x)函數(shù)的花括號{0}表示輸出0號參數(shù)x.GetType()的值,花括號{1}表示輸出1號參數(shù)x的值。在花括號中還可以指定參數(shù)的格式化方式,格式化參數(shù)的設置方法可查閱聯(lián)機幫助。(2) 在程序的最后,插入了一條語句Console::Read();,該語句的作用是等待用戶輸入回車。如果沒有該語句,在編程環(huán)境中運行程序,程序運行后窗口被立即關閉,用戶不能觀察輸出結果,添加該語句后問題得到解決。11.3C++/CLI的句柄、裝箱與拆箱與本地C++不同,CLR的托管內存堆受到垃圾回收器(GarbageCollector)的管理。垃圾回收器使得程序員不必擔心對象是否釋放。CLR的垃圾回收器會定期運行,觀察托管堆中的對象,并判斷它們是否仍然為程序所需。如果不再需要,該對象便被標記為垃圾,最終由垃圾回收器回收所占據(jù)的內存。垃圾回收器不僅用于收回已不再使用的內存空間,還負責內存“碎片”的整理,提高內存的使用效率。
由于垃圾回收器的執(zhí)行,通常CLR程序的執(zhí)行速度要稍慢于人工管理內存的程序。但是,這僅僅在實時性要求高的應用程序中才是問題,對于大多數(shù)對速度不是十分挑剔的程序,自動內存管理所帶來的優(yōu)勢是十分明顯的。11.3C++/CLI的句柄、裝箱與拆箱1.句柄(Handle)
本地C++用new關鍵字為對象在本地堆上分配內存,并且返回一個新分配內存的指針。類似地,托管C++/CLI用gcnew關鍵字為托管對象在托管堆上分配內存,并且返回一個指向這塊新內存的句柄。如同new返回的地址需要一個指針變量保存它一樣,gcnew返回的句柄也需要有相應的變量進行存儲。CLR上的托管堆稱為垃圾回收堆(Garbage-collectedHeap),在其上分配空間的關鍵字gcnew中的“gc”前綴的含義是指垃圾回收堆。11.3C++/CLI的句柄、裝箱與拆箱句柄類似于本地C++指針,但也有很大區(qū)別。句柄確實存儲著托管堆上某個對象的地址,但由于CLR的垃圾回收器會對托管堆進行壓縮整理,可能移動存儲在托管堆中的對象,所以垃圾回收器會自動更新句柄所包含的地址。與本地指針不同的是句柄不能像本地指針那樣執(zhí)行地址的算術運算,也不允許對其進行強制類型轉換。句柄的聲明使用符號^(發(fā)音hat),也不同于指針的聲明使用符號*。與指針變量的定義類似,句柄變量的定義格式如下:<數(shù)據(jù)類型>^<句柄變量名>;例如:Date^myHandle=gcnewDate(2010,4,19);表示用Date類在托管堆上創(chuàng)建對象,并由句柄變量myHandle保存返回的句柄值。11.3C++/CLI的句柄、裝箱與拆箱在本地C++中,&運算符返回一個指針。類似地,在C++/CLI中,%運算符把一個托管對象的內存地址返回給一個句柄。這里需要注意的是:只有當對象位于托管內存中時,才能使用%運算符。下面的代碼在編譯時報告錯誤: int^handle=nullptr; intx=100; handle=&x; //錯誤nullptr是C++/CLI的一個關鍵字,表示向句柄賦空值。這里不能使用本地C++為指針賦空值的0或NULL。C++/CLI中,對句柄也可以使用運算符*和->進行間接訪問。%與*運算符是互逆操作,以任意順序將它們連續(xù)應用于同一個句柄,所產生的作用將相互抵消。11.3C++/CLI的句柄、裝箱與拆箱2.值類型(ValueType)與引用類型(ReferenceType)
區(qū)別于本地C++的數(shù)據(jù)類型,C++/CLI的數(shù)據(jù)類型稱為托管類型。托管類型分值類型和引用類型兩類。.NET框架結構本可以將所有類型設為引用類型,支持值類型的目的是避免處理整型和其他基本數(shù)據(jù)類型時產生不必要的開銷。值類型定義的變量默認情況下是在堆棧上分配空間,但也可以用gcnew操作符將其存儲在托管堆上。引用類型的對象都在托管堆中分配內存空間,然而為引用這些對象所創(chuàng)建的變量都必須是句柄,這些句柄是存儲在堆棧上的。例如,String類型是引用類型,引用String對象的變量必須是句柄。11.3C++/CLI的句柄、裝箱與拆箱在托管堆上分配的對象都不能在全局作用域內聲明,也就是說,全局或靜態(tài)變量的類型不能是托管類型。例如:intx=100; //int為簡單值類型,默認在堆棧上分配空間int^ihandle=gcnewint(123); //ihandle句柄,本身在堆棧上,引用堆上的值類型對象Stringstr;//錯誤!String為引用類型,只能在堆上分配空間,正確格式:String^str="";3.裝箱(Boxing)與拆箱(Unboxing)在C++/CLI中,每種內置數(shù)據(jù)類型都對應一種簡單值類型,如int對應System::Int32。所有的值類型都繼承于System::ValueType類,它是一種輕量級的C++/CLI類機制,非常適合于小型的數(shù)據(jù)結構,且從語義的角度來看,與數(shù)值類似。11.3C++/CLI的句柄、裝箱與拆箱由于System::ValueType類派生于System::Object類,而System::Object類是C++/CLI中所有類的基類,因此所有簡單類型的值都可以賦值給一個Object類型的對象。本質上,值類型無法轉換成引用類型,但可以通過所謂的裝箱操作在托管堆中創(chuàng)建值類型的引用類型副本。裝箱是將值類型的值賦給托管堆上新創(chuàng)建的對象,使該值類型的值可以按照Object類型的對象進行操作。裝箱操作實現(xiàn)了值類型向引用類型的轉換,這種轉換有時是需要的。例如,當將堆棧上的實參變量傳遞給類類型的函數(shù)形參時,編譯器在托管堆上生成含有實參值的對象供函數(shù)調用。裝箱轉換既可以顯式地進行,也可以隱式地自動完成。11.3C++/CLI的句柄、裝箱與拆箱下面例子給出了隱式和顯式裝箱轉換的方法:intx=15; //在堆棧上生成變量x,其中值為15int^ihandle=x;//隱式裝箱,ihandle引用托管堆中的一個對象Object^xhandle=static_cast<Object^>(x); //顯式裝箱拆箱轉換是裝箱的逆操作,拆箱轉換可以把一個Object引用轉換為一個簡單值。拆箱操作就是在堆棧中復制引用類型。通用中間語言CIL包含裝箱與拆箱的指令。例如:inty=static_cast<int>(ihandle); //顯式地拆箱intz=*ihandle; //隱式地拆箱,ihandle是與z相同類型的句柄
如果一個Object句柄實際上并沒有引用一個簡單值類型的值,試圖對其進行拆箱轉換將會導致一個InvalidCastException異常。11.3C++/CLI的句柄、裝箱與拆箱【例11-2】C++/CLI的句柄、值類型與引用類型、裝箱與拆箱等基本概念解析。(a)(b)圖11-2例11-2的內存跟蹤窗口11.3C++/CLI的句柄、裝箱與拆箱跟蹤與觀察:從圖11-2(a)的監(jiān)視1窗口可見,與本地C++類似,簡單值類型變量與句柄的地址前4位相同,表示存儲在內存的同一塊區(qū)域中。句柄intHandle和number中保存的是地址,并且它們在相同的另一塊內存空間中,與本地C++中的指針十分相似,另外這兩個句柄的類型均為System::ValueType^。(2) 從圖11-2(b)的監(jiān)視2窗口可見,句柄intHandle所引用的托管堆中對象的值為210,類型為System::Int32,System::Object^是其基類。對象中含有MaxValue和MinValue值,分別表示Int32能表示的最大值和最小值。MaxValue的值可使用Console::WriteLine(System::Int32::MaxValue);語句輸出。11.4C++/CLI的字符串和數(shù)組
在C++/CLI中,字符串和數(shù)組均屬于引用類型,它們都在托管堆上分配內存。字符串是System::String類的對象,所有的數(shù)組對象都是隱式地繼承于System::Array類。11.4.1C++/CLI中的String類
C++/CLI中用String類類型來說明字符串,所定義的字符串是Unicode字符的有序集合,用于表示文本。每個Unicode字符占用內存中的兩個字節(jié),String對象是System::Char對象的有序集合。String對象的值是不可變的,一旦創(chuàng)建了該對象,就不能修改該對象的值。看似修改了String對象的方法實際上是返回一個包含修改內容的新String對象。如果需要修改字符串對象的實際內容,應使用System::Text::StringBuilder類。與本地C++一樣,C++/CLI中的String對象是以null字符結尾,這使得String對象和本地C++字符串與字符數(shù)組具有極強的互操作性。與其他引用類型對象的聲明相同,String對象的定義方式如下: String^msg="WellcometoC++/CLIprogramming!"; String^errorStr=gcnewString('輸入錯誤!');String類可以使用類似于本地字符數(shù)組的方法索引字符串中的元素。此外,還提供了許多成員函數(shù)和運行符重載函數(shù)實現(xiàn)字符串的復制、合并、比較、查找和替換等操作。下面通過示例演示String類的基本用法,更詳盡的用法可參考聯(lián)機幫助?!纠?1-3】String類的基本用法。11.4C++/CLI的字符串和數(shù)組程序說明:(1) 程序的Console::WriteLine(“…is{0:B}”,…);語句中,{0:B}表示輸出格式為布爾值。此處使用了.NET框架的復合格式設置功能,將對象的值轉換為其文本表示形式,并將該表示形式嵌入字符串中。格式項的語法是{索引[,對齊方式][:格式字符串]},它指定了一個強制索引、格式化文本的可選長度和對齊方式,以及格式說明符字符的可選字符串,其中格式說明符字符用于控制如何設置相應對象的值的格式。例如:doublevalue=123.456;Console::WriteLine("value={0:C2}",value); //貨幣格式輸出Console::WriteLine("value={0:E}",value); //科學記數(shù)法格式輸出輸出內容為:value=¥123.46value=1.234560E+00211.4C++/CLI的字符串和數(shù)組11.4C++/CLI的字符串和數(shù)組(2)在程序的最后幾個輸出語句中,用String類的Replace和ToLower成員函數(shù)對字符串的內容進行替換或變換,但這種修改是不改變原有字符串中的信息的,運行結果的最后一行驗證了myString3的內容沒有改變。11.4.2C++/CLI中的數(shù)組C++/CLI數(shù)組與String一樣是引用類型,在托管堆上程序可以定義一維數(shù)組、多維數(shù)組和不規(guī)則數(shù)組。與其他引用類型相同,托管堆中的數(shù)組也需要通過句柄來訪問。11.4C++/CLI的字符串和數(shù)組C++/CLI中使用array關鍵字定義托管數(shù)組,一維數(shù)組的定義方式://定義有10個單元的整型數(shù)組,每個單元的值為0array<int>^int1DArray=gcnewarray<int>(10);//定義有5個單元的整型數(shù)組,單元值依次為1、3、5、7、9array<int>^values={1,3,5,7,9};//定義20個單元的實型數(shù)組,每個單元值為0.0array<double>^doubleArray(gcnewarray<double>(20));多維數(shù)組的定義方法與一維數(shù)組類似。一維數(shù)組實際上是維數(shù)為1的多維數(shù)組,缺省情況下,托管數(shù)組定義語句中的維數(shù)值即為1。多維數(shù)組的最大維數(shù)值為32。多維托管數(shù)組的定義方法://定義4行5列,共20個單元的二維整型數(shù)組array<int,2>^int2DArray=gcnewarray<int,2>(4,5);//定義48個單元的三維整型數(shù)組array<int,3>^int3DArray=gcnewarray<int,3>(2,4,6);11.4C++/CLI的字符串和數(shù)組一維數(shù)組存儲單元的訪問方式與本地C++數(shù)組一樣,方括號中加索引值的方式引用存儲單元,并且索引的初值也是0。例如int1DArray[0]和int1DArray[3]表示訪問數(shù)組的第1和第4個存儲單元。多維數(shù)組存儲單元的訪問方式與本地C++數(shù)組不同,采用在一個方括號內用逗號分隔索引值的方法。例如int2DArray[0,0]和int3DArray[1,3,4]。不規(guī)則數(shù)組有時又稱為數(shù)組的數(shù)組、變長數(shù)組或正交數(shù)組。相對地,上面所講的多維數(shù)組又稱為矩形數(shù)組。不規(guī)則數(shù)組與本地C++在堆上創(chuàng)建動態(tài)數(shù)組的方法相似,它是通過句柄、句柄數(shù)組和一維數(shù)組構建長度不等的托管數(shù)組。11.4C++/CLI的字符串和數(shù)組下面的語句是在托管堆中建立了一個行長度不等的二維數(shù)組,其中第0行有兩個元素,第1行有一個元素,第2行有三個元素:
array<array<int>^>^jagged={ gcnewarray<int>{1,2}, gcnewarray<int>{3}, gcnewarray<int>{4,5,6} };jagged是保存在堆棧中的句柄,它引用了托管堆上有3個元素的array<int>類型句柄數(shù)組,該數(shù)組的每個單元存儲了一個array<int>類型的句柄,每個句柄所引用的一維數(shù)組的長度互不相同,分別為2、1和3,故稱為不規(guī)則數(shù)組。所有數(shù)組若訪問越界,則會引發(fā)異常。11.4C++/CLI的字符串和數(shù)組例如,由于int3DArray數(shù)組中沒有int3DArray[2,3,4]單元,語句int3DArray[2,3,4]=10;將導致CLR拋出IndexOutOfRangeException類型的異常。類似地,語句jagged[1][1]=10;也引發(fā)相同的異常。foreach語句是C++/CLI新引入的一種循環(huán)語句,這種語句可用于對整個數(shù)組或集合進行遍歷。語法格式如下: foreach(<迭代變量數(shù)據(jù)類型><迭代變量>in<數(shù)組或容器>) <循環(huán)體>下面的程序段演示了用foreach遍歷數(shù)組元素的方法: array<int>^values={1,3,5,7,9}; inttotal=0; foreach(intnumberinvalues) total+=number;11.4C++/CLI的字符串和數(shù)組在CLR中,System::Array類是所有數(shù)組的基類,并且其中定義了對數(shù)組進行排序和查找的方法,用戶在程序中能非常方便地對托管數(shù)組進行排序或查找等操作?!纠?1-4】托管數(shù)組的應用示例。程序說明:(1)用Array::Sort函數(shù)可對數(shù)組進行排序。Array中的Sort函數(shù)有個重載版本,具有對兩個數(shù)組進行同步排序的功能。Array中另一個有用的函數(shù)是BinarySearch,其功能是在一維數(shù)組中搜索指定元素,并返回元素的索引位置。(2)二維數(shù)組matrix元素索引采用[i,j]方式,與本地C++的[i][j]方式不同,托管多維數(shù)組的數(shù)據(jù)是依次連續(xù)地存儲在一個段中,訪問元素的速度可能要快于不規(guī)則數(shù)組。11.5C++/CLI中的類和屬性C++/CLI中可以定義兩種結構或類類型,一種為數(shù)值類類型(ValueClassType)和數(shù)值結構類型(ValueStructType);另一種是引用類類型(RefClassType)和引用結構類型(RefStructType)。與本地C++一樣,結構類型與類類型的區(qū)別在于struct類型的缺省訪問控制是公有的,而class類型的缺省訪問控制是私有的。本節(jié)主要討論數(shù)值類和引用類。雙字關鍵字valueclass用于聲明數(shù)值類類型,refclass用于聲明引用類類型。數(shù)值類類型定義的對象可以分配在堆棧、本地堆或托管堆中,引用類聲明的對象只能在托管堆中分配空間。11.5C++/CLI中的類和屬性引用類的定義方式與本地C++類基本相同,例如: refclassBox{ public: Box(); ... private: doublelength; doublewidth; doubleheight; };C++/CLI的類比本地C++中的類增加了屬性成員,語法上用property關鍵字聲明類的屬性。例如propertydoublevalue。屬性的引入省略了本地C++類中為存取私有數(shù)據(jù)成員編寫類似set或get的成員函數(shù)。屬性提供了更方便、更清晰的語法來訪問或修改類的數(shù)據(jù)成員。11.5C++/CLI中的類和屬性在用法上,類中的屬性與成員變量比較相似,其實它們有質的區(qū)別,主要區(qū)別在于:變量名引用了某個存儲單元,而屬性名則是調用某個函數(shù)。屬性擁有訪問屬性的set()和get()函數(shù),并且函數(shù)名必須是get或set。當使用屬性名進行讀取或賦值時,實際上在調用該函數(shù)的get()或set()函數(shù)。如果一個屬性僅提供了get()函數(shù),則它是只讀屬性;如果一個屬性僅提供set()函數(shù),則它是只寫屬性。C++/CLI的類可以有兩種不同的屬性:標量屬性(ScalarProperties)和索引屬性(IndexProperties)。標量屬性是指通過屬性名來訪問的單值;索引屬性是利用屬性名加方括號來訪問的一組值。例如String類,其Length屬性為標量屬性,用object->Length來訪問其長度,且Length是個只讀屬性。String還包含了索引屬性,可以用object[idx]來訪問字符串中第idx個字符。11.5C++/CLI中的類和屬性屬性可以與類的實例(類對象)相關,此時屬性被稱為實例屬性(InstanceProperties),如String類的Length屬性;如果用static修飾符指定屬性,則屬性為類屬性,類的所有對象在該屬性項上都具有相同的屬性值?!纠?1-5】設計人民幣數(shù)值類和商品引用類,在類中應用屬性訪問私有數(shù)據(jù)成員。11.5C++/CLI中的類和屬性程序說明:(1) 數(shù)值類RMB對象可以存儲在堆棧、本地堆和托管堆上,而引用類Goods的對象只能存儲在托管堆上。RMBmyRMB定義的對象保存在堆棧上,Goodscomputer(...)所定義的對象保存在托管堆中,computer僅保存了它的引用地址。語句Goods^gHandle=gcnewGoods(...)能正確運行,而語句Goods*goodsPtr=newGoods(...)將導致編譯錯誤。valueclass類是System::ValueType的派生類,refclass類是繼承于System::Object的。(2) C++/CLI中的函數(shù)不能有默認參數(shù)。若為RMB類的構造函數(shù)指定默認參數(shù),例如語句RMB(inty,intj,intf=0),在程序編譯時報告錯誤信息如下:無法為托管類型或泛型函數(shù)的成員函數(shù)聲明默認參數(shù)。11.5C++/CLI中的類和屬性(3) 語句myRMB.value=2345.56;是通過屬性設置類中私有數(shù)據(jù),computer.Name是讀取屬性Name中的值,computer.Price是讀取Goods類對象中私有數(shù)據(jù)price的值。Goods類中定義了屬性Name和Price。Name屬性沒有定義set和get,也沒有與之對應的私有數(shù)據(jù)成員name。Price屬性只定義get函數(shù),沒有定義set函數(shù),為只讀屬性。下面為屬性賦值的語句第一條能正常運行,第二條在編譯時報錯:computer.Name="Dellvostro3700"; //正確computer.Price=8908.90;//錯誤!因為Goods::Price沒有定義set
以跟蹤方式運行程序,觀察computer對象,可見該對象中多了一個由系統(tǒng)為其添加的成員變量<backing_store>Name,其中保存了Name屬性值:“聯(lián)想V470G-ISE”。11.6C++/CLI中的多態(tài)與接口C++/CLI中的多態(tài)采用了與本地C++相同的實現(xiàn)方式。在語法上,C++/CLI要求顯式地聲明虛函數(shù)和抽象類。C++/CLI中的虛函數(shù)要求在派生類中用virtual關鍵字顯式地聲明,并用override關鍵字聲明為重載函數(shù)。下面的代碼段說明了虛函數(shù)的聲明方法。Shape類為基類,Circle類為派生類,其中虛函數(shù)draw為重載函數(shù): refclassShape{ public: virtualvoiddraw(); }; refclassCircle:Shape{ public: virtualvoiddraw()override; }11.6C++/CLI中的多態(tài)與接口派生類的函數(shù)還可以用new關鍵字指明沒有重寫這個函數(shù)的基類版本,它隱藏了基類的相同函數(shù)。例如,Circle類中的draw函數(shù)聲明為new: virtualvoiddraw()new;此時,下面的語句所調用的draw函數(shù)將來自不同的類: Shape^shandle=gcnewCircle(); shandle->draw(); //調用Shape的draw函數(shù) CirclecircleObj; circleObj.draw(); //調用Circle的draw函數(shù)這里的new關鍵字是上下文敏感的,只在托管類型的函數(shù)聲明中才充當該角色。關鍵字sealed用于指明類或函數(shù)不能被重寫。Shape中的draw函數(shù)如果聲明為voiddraw()sealed;,則任何派生類都不允許重寫draw函數(shù)。如果以refclassShapesealed{};方式聲明Shape類,則Shape類不能作為基類,其所有成員函數(shù)都被隱式地聲明為sealed函數(shù)。11.6C++/CLI中的多態(tài)與接口抽象類在本地C++中是指含有純虛函數(shù)的類,純虛函數(shù)的聲明是在后面添加“=0”。在C++/CLI中依然使用這種風格的聲明,同時又添加了一個關鍵字abstract作為替代方案。下面的純虛函數(shù)聲明在C++/CLI中是等價的: virtualvoiddraw()abstract; virtualvoiddraw()=0;程序員可以用abstract關鍵字聲明類的抽象類,例如refclassShapeabstract{...};。聲明為abstract的托管類并不會隱式地將類中的任何函數(shù)聲明為abstract。與本地C++不同,托管的抽象類并不一定要包含純虛函數(shù),可以為抽象類中的每個函數(shù)定義一個實現(xiàn),供所有派生類使用。抽象類中的純虛函數(shù)必須用abstract或“=0”進行聲明。11.6C++/CLI中的多態(tài)與接口C++/CLI還支持名字重寫。該技巧允許派生類重寫它繼承的虛函數(shù),并為這個函數(shù)提供一個新的名稱。例如,在Circle類中可以用一個新的函數(shù)display對draw函數(shù)進行重寫: refclassCircle:Shape{ public: virtualvoiddisplay()=Shape::draw; };使用名字重寫技巧對類層次結構高層的多個函數(shù)進行重寫,在復雜的類層次結構中有時使用這個技巧會帶來方便。11.6C++/CLI中的多態(tài)與接口接口(interface)是C++/CLI新引入的概念。雖然接口的定義方式與托管類的定義比較相似,但兩者完全不同。接口本質上是一種類,其中聲明了一組由其他類來實現(xiàn)的函數(shù),數(shù)值類和引用類都能實現(xiàn)接口中的函數(shù)。接口不實現(xiàn)它的函數(shù)成員,而是在繼承于該接口的派生類中定義它們。關鍵字interfaceclass用于聲明接口,無需聲明,接口中的函數(shù)均是純虛函數(shù)。接口聲明方式如下,通常接口名用大寫字母I開頭: interfaceclassIMyInterface{ voidTest(); voidShow(); };11.6C++/CLI中的多態(tài)與接口C++/CLI不同于本地C++,類的繼承只支持單繼承,并不支持類的多重繼承,而接口支持多重繼承,因此一個類可通過繼承多個接口,實現(xiàn)與多重繼承相似的功能。接口中含有函數(shù)、事件和屬性的聲明,并且它們的訪問權限均為公有的。接口中還可以有靜態(tài)成員(數(shù)據(jù)成員、函數(shù)、事件和屬性),這些靜態(tài)成員必須在接口中定義?!纠?1-6】接口及其實現(xiàn)示例。11.6C++/CLI中的多態(tài)與接口程序說明:(1)接口中的函數(shù)默認為純虛函數(shù),IShape和IContainer中的函數(shù)均沒有顯式地聲明為virtual,但在實現(xiàn)接口函數(shù)的Circle、Rectangle和Cuboid類中需要顯式地聲明為虛函數(shù)。IShape中聲明了Area屬性,接口中還可以聲明事件。Cuboid類實現(xiàn)了IShape和IContainer接口。引用類可以在繼承另一個類的同時實現(xiàn)多個接口。11.6C++/CLI中的多態(tài)與接口例如,在本例程中可添加圓柱類如下:refclassCylinder:publicCircle,IShape,IContainer{public: Cylinder(doubleh,doubler):hight(h),Circle(r){} propertydoubleArea{ virtualdoubleget()new{ return2*Circle::Area+hight*2*PI*Radius; } } virtualvoidShowMSG()override{ Console::WriteLine(L"圓柱體的底面半徑為:{0},高為{1},表面積為:{2},體積為:{3}",Radius,hight,Area,Volume()); } virtualdoubleVolume(){ returnCircle::Area*hight; }private: doublehight;};11.7C++/CLI中的模板和泛型在C++/CLI中,可以如同本地C++一樣創(chuàng)建并使用托管類模板和函數(shù)模板。例如,棧類在托管代碼中可如下聲明: template<typenameT> refclassManagedStack{ ... }; 托管類模板完全支持本地類模板的所有特性,如非模板參數(shù)和顯式實例化。與托管類一樣,托管類模板也不能聲明其他類或函數(shù)作為自己的友元,但可以被聲明為本地類的友元。11.7C++/CLI中的模板和泛型在C++/CLI中,提供了一種與本地C++模板非常相似的代碼復用技術,稱為泛型(Generic)。C++/CLI中不僅能定義泛型數(shù)值類、泛型引用類和泛型函數(shù),還能聲明泛型接口類和泛型委托。聲明泛型類的語法類似于托管類模板,關鍵字template用關鍵字generic替換。例如:generic<typenameT>refclassStack{...};泛型是由公共語言運行時(CLR)定義的,具有跨程序集的能力,泛型類和泛型函數(shù)可被其他.NET語言(如C#)所編寫的代碼使用。11.7C++/CLI中的模板和泛型泛型與模板有許多相似之處,但它們實際上存在質的區(qū)別。主要區(qū)別如下:泛型是在運行時實例化,而模板是在編譯時實例化。泛型類型無法作為模板類型參數(shù),而模板類型可以作為泛型類型參數(shù)。泛型使用類型約束限制在泛型代碼中可以使用的類型。泛型類型參數(shù)必須是引用類型的句柄、接口類型句柄或值類型,不支持非類型參數(shù)或默認值。11.7C++/CLI中的模板和泛型C++/CLI提供了幾種類型約束。類約束表示類型實參必須是一個特定基類或其子類的對象。接口約束表示類型實參必須已實現(xiàn)某特定的接口。下面的代碼聲明了一個含有約束限制的泛型函數(shù): generic<typenameT>whereT:IComparable TMaxElement(array<T>^x){ Tmax(x[0]); for(inti=1;i<x->Length;i++) if(max->CompareTo(x[i])<0) max=x[i]; returnmax; }11.7C++/CLI中的模板和泛型generic<typenameT>后面的whereT:IComparable是類型約束,指明T必須實現(xiàn)接口IComparable。實現(xiàn)了IComparable接口的類型必須定義一個CompareTo成員函數(shù),對同種類型的對象進行比較。泛型函數(shù)MaxElement中使用該函數(shù)來判定數(shù)組中第i個單元的元素是否大于max。如果在泛型聲明時沒有指定類型約束,默認的約束是Object。泛型的一個類型參數(shù)可以應用多個約束,方法是在where分句中用逗號分隔多個約束形成約束列表?!纠?1-7】使用泛型技術設計鏈棧,并測試。
11.8C++/CLI中的異常C++/CLI中的異常與本地C++異常處理十分相似。在托管代碼中,System::Exception類是所有異常類的基類,系統(tǒng)只捕獲并處理由Exception類及其子類拋出的異常?;怑xception派生了兩個重要的異常類:SystemException類和ApplicationException類。SystemException的派生類預定義了公共語言運行時異常類,例如:數(shù)組越界訪問CLR拋出IndexOutOfRangeException類,引用不存在的對象時CLR拋出NullReferenceException異常類。ApplicationException類是程序發(fā)生非致命應用程序錯誤時引發(fā)的異常類,系統(tǒng)用它區(qū)分應用程序定義的異常與系統(tǒng)定義的異常。用戶應用程序可定義并引發(fā)從ApplicationException類派生的自定義異常類。11.8C++/CLI中的異常Exception異常類包含很多屬性,可以幫助標識異常的代碼位置、類型、幫助文件和原因。Exception中的屬性有StackTrace、InnerException、Message、HelpLink、HResult、Source、TargetSite和Data等。Message屬性存儲了當前異常的錯誤消息。StackTrace屬性返回源于異常引發(fā)位置的調用堆棧的框架。當兩個或多個異常之間存在因果關系時,InnerException屬性會維護此信息。關于異常的補充信息可以存儲在Data屬性中。try-catch格式語句在C++/CLI中依然有效,此外,C++/CLI還引入了新關鍵字finally,支持try-finally和try-catch-finally兩種格式的語句。11.8C++/CLI中的異常try-catch-finally語句是在try-catch語句后加上finally代碼段,其中catch語句同樣可以有多個,但finally語句只能有一個并且在所有catch語句之后。try語句拋出的異常依然由不同的catch語句捕獲并處理。無論異常是否發(fā)生,finally語句中的代碼段總是被執(zhí)行。finally代碼段中通常是程序必須執(zhí)行的任務,如資源釋放、關閉文件等。finally語句的優(yōu)先級較高,即使之前的try或catch語句的代碼段中使用了break、continue等跳轉語句,finally代碼段都要被執(zhí)行。此外,finally代碼段中不能使用return語句,break和continue語句也只能在代碼段中跳轉,否則編譯器將報告錯誤。C++/CLI的異常機制也是使用throw拋出異常。與本地C++不同,C++/CLI的throw語句只能拋出Exception及其派生類對象的引用。除此限制之外,用法與本地C++拋出異常的方法相似。下面的語句給出了一個典型的異常拋出方法:throwgcnewException("error!");11.8C++/CLI中的異?!纠?1-8】try-catch-finally語句應用示例。
程序說明:(1) 語句x=int::Parse(Console::ReadLine());中的int::Parse函數(shù)是將從鍵盤輸入的數(shù)值內容的字符串轉換為int類型整數(shù)。如果字符串內容為空,則拋出ArgumentNullException異常。如果字符串內容不是數(shù)值,則拋出FormatException異常。如果字符串內容超出int類型所能表示的值區(qū)間,則拋出OverflowException異常。(2) 異常處理在提高程序容錯能力的同時,也會造成程序的性能、可讀性和可維護性的下降。通常在程序的高層次處理異常,在編寫底層方法、模塊或組件時,盡量少用或者不用異常處理,而是在調用它們的代碼中執(zhí)行異常處理。11.9C++/CLI中的枚舉C++/CLI的托管枚舉類型與本地C++在聲明和訪問方式上有一些不同。用關鍵字enumclass聲明托管枚舉類型。例如:enumclassWeek{Mon=1,Tues,Wed,Thurs,Fri,Sat,Sun}枚舉類型中的枚舉常量是對象,不再是本地C++中使用的整數(shù)值。雖然在默認情況下,枚舉常量是Int32值類型的對象,但C++/CLI不允許直接將枚舉常量與整數(shù)或其他簡單類型進行算術運算,除非用safe_cast顯式地轉換為整數(shù)。在聲明枚舉類型時,允許修改枚舉常量所封裝的數(shù)據(jù)類型。下面語句用char類型替換了默認的Int32類型,方法是在枚舉類型名的后面加注“:char”:enumclassSuit:char{Clubs='C',Diamonds='D',Hearts='H',Spades='S'};11.9C++/CLI中的枚舉不同于本地C++,托管枚舉變量的賦值需要在枚舉常量的前面加上枚舉名和作用域解析運算符。例如:Weektoday=Week::Fri; //Weektoday=Fri;為錯誤!可以用“++”和“--”運算符對枚舉變量進行增加或減小:toady++; //today中內容為Week::Sat用關系運算符(==、!=、<、<=、>、>=)可以對枚舉變量進行邏輯運算:today==Week::Mon //若today中值為Week::Mon表達式值為真,否則為假
枚舉的一個重要用途是設置標志位,稱為標志枚舉。托管枚舉變量同樣能用于程序運行時狀態(tài)的標志。與本地C++相同,用&、|和~運算符也可以對標志位進行設置或清除。11.9C++/CLI中的枚舉下面的代碼段給出了標志枚舉類型的定義、位設置、位清除及標志位判別的方法:enumclassWindowStyle{ //窗口狀態(tài)枚舉類型
MINIMUM_BUTTON=1, //十六進制表示為0x0001MAXIMUM_BUTTON=2,CLOSE_BUTTON=4 }//ws變量記錄窗口狀態(tài),窗口既有MINIMUM_BUTTON又有CLOSE_BUTTON按鈕WindowStylews=WindowStyle::MINIMUM_BUTTON|WindowStyle::CLOSE_BUTTON;//窗口關閉MINIMUM_BUTTON按鈕,清除MINIMUM_BUTTON標志位ws=ws&~WindowStyle::MINIMUM_BUTTON//判別窗口是否有CLOSE_BUTTON按鈕(ws&WindowStyle::CLOSE_BUTTON)==WindowStyle::CLOSE_BUTTON11.9C++/CLI中的枚舉【例11-9】枚舉類型變量應用示例。程序說明:(1) 枚舉類型Suit在聲明時為每個枚舉常量指定了一個字符值。程序運行結果的第一行輸出的整型值為67,它是字母C的ASCII碼值。(2) 枚舉類型FlagBits聲明語句前的[Flags]是用于告知編譯器枚舉常量是簡單的位值。去除該項,程序運行結果的第2行將輸出:枚舉變量flags的值:5,轉換為int類型的值:511.10.NET中的委托與事件11.10.1委托委托(delegate)是一種托管對象,其中封裝了對一個或多個函數(shù)的類型安全的引用。委托是基于面向對象的封裝思想,將一個或多個指向函數(shù)的指針封裝在對象中。委托的功能在某些方面類似于本地C++的函數(shù)指針,但委托是面向對象的,并且是類型安全的。委托的聲明使用關鍵字delegate外加函數(shù)原型的方式,例如:publicdelegatevoidFunDelegate(int);//FunDelegate為委托類型每個委托實際上都是一個獨立的類,它們派生于System::MultiCastDelegate類,而后者又是派生于System::Delegate類。所有委托最終都繼承了System::Delegate類的成員函數(shù),這些成員函數(shù)中有一個Invoke函數(shù),該函數(shù)的返回類型和參數(shù)與委托所聲明的函數(shù)相同。11.10.NET中的委托與事件與引用類相似,委托的使用也需要定義一個委托對象,并且也是用gcnew生成一個托管對象。委托對象定義方式如下:FunDelegate^funHandler=gcnewFunDelegate(myClass::staticMbeFun);//定義委托這里,funHandler為委托句柄,引用了托管堆中的一個委托對象。委托封裝了myClass類的靜態(tài)成員函數(shù)staticMbeFun,并且該函數(shù)的形參和返回類型與委托聲明中的函數(shù)原型相一致。對于類的非靜態(tài)成員函數(shù),在定義委托時需要調用委托的雙參構造函數(shù),并傳遞預先已定義的類對象和類的成員函數(shù)。方法如下:myClass^Obj=gcnewmyClass; //定義托管對象FunDelegate^funHandler=gcnewFunDelegate(Obj,&myClass::mbFun);11.10.NET中的委托與事件其中,mbFun為myClass類的成員函數(shù),并且與委托聲明中的函數(shù)原型匹配。本地C++的函數(shù)指針一次只能指向一個函數(shù),而委托不受這種限制。MultiCastDelegate類通過存儲一個委托實例的鏈表(稱為委托的調用列表),允許一個委托同時封裝多個函數(shù)。向調用列表添加委托實例的方法是使用已經重載的+=運算符,從調用列表刪除委托實例的方法是使用重載的-=運算符。例如:funHandler+=gcnewFunDelegate(othObj,&OtherClass::fun); //添加委托實例funHandler-=gcnewFunDelegate(Obj,&myClass::mbFun); //刪除委托實例11.10.NET中的委托與事件通過委托對象可以方便地調用封裝于委托調用列表中的函數(shù),調用方法有兩種:一種是直接通過委托句柄調用,另一種是調用Invoke函數(shù)。例如:funHandler(100); //通過委托句柄調用funHandler->Invoke(200); //通過Invoke函數(shù)調用在一次委托調用過程中,委托調用列表中所指向的函數(shù)均被調用執(zhí)行。從功能上,委托似乎只是一種間接地調用函數(shù)的方法。實事上,委托在許多場合是十分有用的,委托既可以作為參數(shù)傳遞給函數(shù),也可以用于將函數(shù)從一個類傳遞給另一個類。委托是一個定義了方法的類類型,利用它可以將方法當作另一個方法的參數(shù)來進行傳遞,這種將方法動態(tài)地賦給參數(shù)的做法,可以避免在程序中大量使用分支語句,同時使得程序具有更好的可擴展性。11.10.NET中的委托與事件【例11-10】委托聲明、定義與調用方法示例。程序說明:(1) 程序中定義了一個sumDelegate委托類型,用其定義了托管委托
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 科室安裝監(jiān)控合同范本
- 二零二五個人抵押反擔保合同書
- 移動渠道經理工作指南
- 工裝合同范本詳細
- 二零二五知識產權歸屬協(xié)議書格式簡短
- 門窗承包簡單合同范例二零二五年
- 育肥豬訂購合同范本
- 糖尿病護理查房
- 鐵路教育情況匯報
- 血液透析中低血壓的管理
- DB33T 2216-2019 人民調解工作規(guī)范
- 2025年度板材模板產品認證與質量保證協(xié)議3篇
- 2025版風力發(fā)電機采購合同-環(huán)保節(jié)能產品協(xié)議3篇
- 2025上海奉賢區(qū)南橋鎮(zhèn)大學生村官招聘20人歷年高頻重點提升(共500題)附帶答案詳解
- 防大風應急預案及措施
- 《胃癌專病隊列數(shù)據(jù)集建設規(guī)范要求(征求意見稿)》
- 2025年國家糧食和物資儲備局垂直管理系統(tǒng)事業(yè)單位招聘701歷年管理單位筆試遴選500模擬題附帶答案詳解
- 市政道路工程施工安全教育
- 四川省雙流縣彭鎮(zhèn)初級中學-主題班會-元旦互動游戲【課件】
- JJF(陜) 036-2020 單相機攝影測量系統(tǒng)校準規(guī)范
- 《AHA2023心肺復蘇與心血管急救指南》解讀課件
評論
0/150
提交評論