隨筆-208  評論-469  文章-30  trackbacks-0
          C++語言概述 C++語言是一種應(yīng)用較廣的面向?qū)ο蟮某绦蛟O(shè)計(jì)語言,使用它可以實(shí)現(xiàn)面向?qū)ο蟮某绦蛟O(shè)計(jì)。面向?qū)ο蟮脑O(shè)計(jì)與面向過程的設(shè)計(jì)是有很大區(qū)別的,面向?qū)ο蟮某绦蛟O(shè)計(jì)是在面向過程的程序設(shè)計(jì)的基礎(chǔ)上一個(gè)質(zhì)的飛躍。要學(xué)會面向?qū)ο蟮某绦蛟O(shè)計(jì),首先要學(xué)會一種面向?qū)ο蟮恼Z言,即要學(xué)會用VC編程,就要先有C++的基礎(chǔ),而學(xué)習(xí)C++語言首先要認(rèn)識它面向?qū)ο蟮奶匦院蛯?shí)現(xiàn)面向?qū)ο蟮姆椒ā?   C++是一種面向?qū)ο蟮某绦蛟O(shè)計(jì)語言   當(dāng)你首次學(xué)習(xí)C++時(shí),總會碰到一些在C語言從未見過的概念,如:類、對象、抽象、封裝、繼承、多態(tài)性、虛函數(shù)等等。這些概念是C++所具有,下面簡單的介紹一下C++對面向?qū)ο蟪绦蛟O(shè)計(jì)方法的支持和實(shí)現(xiàn)。   1、C++支持?jǐn)?shù)據(jù)封裝   支持?jǐn)?shù)據(jù)封裝就是支持?jǐn)?shù)據(jù)抽象。在C++中,類是支持?jǐn)?shù)據(jù)封裝的工具,對象則是數(shù)據(jù)封裝的實(shí)現(xiàn)。面向過程的程序設(shè)計(jì)方法與面向?qū)ο蟮某绦蛟O(shè)計(jì)方法在對待數(shù)據(jù)和函數(shù)關(guān)系上是不同的,在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,將數(shù)據(jù)和對該數(shù)據(jù)進(jìn)行合法操作的函數(shù)封裝在一起作為一個(gè)類的定義,數(shù)據(jù)將被隱藏在封裝體中,該封裝體通過操作接口與外界交換信息。對象被說明具有一個(gè)給定類的變量,類類似于C語言中的結(jié)構(gòu),在C語言中可以定義結(jié)構(gòu),但這種結(jié)構(gòu)中包含數(shù)據(jù),而不包含函數(shù)。C++中的類是數(shù)據(jù)和函數(shù)的封裝體。在C++中,結(jié)構(gòu)可作為一種特殊的類,它雖然可以包含函數(shù),但是它沒有私有或保護(hù)的成員。   2、C++類中包含私有、公有和保護(hù)成員   C++類中可定義三種不同訪控制權(quán)限的成員。一種是私有(Private)成員,只有在類中說明的函數(shù)才能訪問該類的私有成員,而在該類外的函數(shù)不可以訪問私有成員;另一種是公有(Public)成員,類外面也可訪問公有成員,成為該類的接口;還有一種是保護(hù)(Protected)成員,這種成員只有該類的派生類可以訪問,其余的在這個(gè)類外不能訪問。   3、C++中通過發(fā)關(guān)消息來處理對象   C++中是通過向?qū)ο蟀l(fā)關(guān)消息來處理對象的,每個(gè)對象根據(jù)所接收到的消息的性質(zhì)來決定需要采取的行動,以響應(yīng)這個(gè)消息。響應(yīng)這些消息是一系列的方法,方法是在類定義中使用函數(shù)來定義的,使用一種類似于函數(shù)調(diào)用的機(jī)制把消息發(fā)送到一個(gè)對象上。   4、C++中允許友元破壞封裝性   類中的私有成員一般是不允許該類外面的任何函數(shù)訪問的,但是友元便可打破這條禁令,它可以訪問該類的私有成員(包含數(shù)據(jù)成員和成員函數(shù))。友元可以是在類外定義的函數(shù),也可以是在類外定義的整個(gè)類,前者稱友元函數(shù),后者稱為友元類。友元打破了類的封裝性,它是C++另一個(gè)面向?qū)ο蟮闹匾?   5、C++允許函數(shù)名和運(yùn)算符重載   C++支持多態(tài)性,C++允許一個(gè)相同的標(biāo)識符或運(yùn)算符代表多個(gè)不同實(shí)現(xiàn)的函數(shù),這就稱標(biāo)識符或運(yùn)算符的重載,用戶可以根據(jù)需要定義標(biāo)識符重載或運(yùn)算符重載。   6、C++支持繼承性   C++中可以允許單繼承和多繼承。一個(gè)類可以根據(jù)需要生成派生類。派生類繼承了基類的所有方法,另外派生類自身還可以定義所需要的不包含在父類中的新方法。一個(gè)子類的每個(gè)對象包含有從父類那里繼承來的數(shù)據(jù)成員以及自己所特有的數(shù)據(jù)成員。   7、C++支持動態(tài)聯(lián)編   C++中可以定義虛函數(shù),通過定義虛函數(shù)來支持動態(tài)聯(lián)編。   以上是所講的是C++對面向?qū)ο蟪绦蛟O(shè)計(jì)中的一些主要特征的支持。   C++的詞法及詞法規(guī)則   1、C++的字符集   字符是一些可以區(qū)分的最小符號。C++的字符集由大小寫英文字母(a-z和A-Z)、數(shù)據(jù)字符(0-9)、特殊字符(空格,!,#,%,^,&,*,_,<,>,?,\,,)組成。   2、單詞及詞法規(guī)則   單詞又稱詞法記號,它是由若干個(gè)字符組成的具有一定意義的最小詞法單元。C++共有6種單詞,分別是:標(biāo)識符、關(guān)鍵字、運(yùn)算符、分隔符、常量、注釋符,在編碼時(shí)要特別注意這些單詞的詞法規(guī)則。要注意的是C++中的空白符:C++中經(jīng)常使用空白符,實(shí)際上,空白符不是一個(gè)字符,它是空格符、換行符和水平制表符的統(tǒng)稱。注意,空白符不等于空格符,只是空白符包含空格符。還有一個(gè)空字符,要把它與空白符分開。空字符是指ASCII碼值為0的那個(gè)字符。空字符在C++中有特殊用途,用它來作為字符串的結(jié)束符。存放在內(nèi)存中的字符串常量都在最后有一個(gè)結(jié)束符,即用空字符,它用轉(zhuǎn)義序列方法表示為’\0’。   C++程序結(jié)構(gòu)的組成   C++程序結(jié)構(gòu)的基本組成部分   1 預(yù)處理命令,C++提供了三類預(yù)處理命令:宏定義命令、文件包含命令、條件編譯命令。   2 輸入輸出,C++程序中總是少不了輸入和輸出的語句,實(shí)現(xiàn)與程序內(nèi)部的信息交流。特別是屏幕輸出的功能,幾乎每個(gè)程序都要用到,使用它把計(jì)算機(jī)的結(jié)果顯示在屏幕上。   3 函數(shù),C++的程序是由若干個(gè)文件組成的,每個(gè)文件又是由若干個(gè)函數(shù)組成,因此,可以認(rèn)為C++的程序就是函數(shù)串,即由若干個(gè)函數(shù)組成,函數(shù)與函數(shù)之間是相對的,并且是并行的,函數(shù)之間可以調(diào)用。在組成一個(gè)程序的若干個(gè)函中,必須有一個(gè)main()。   4 語句,語句是組成程序的基本單元。函數(shù)是由若干條語句組成的。但是,空函數(shù)是沒有語句的。語句是由單詞組成,單詞間用空格符分隔,C++程序中的語句又是以以分號結(jié)束。語句除了有表達(dá)式語句和空語句之外,還有復(fù)合語句、分支語句、循環(huán)語句和轉(zhuǎn)向語句等若干類。   5 變量,多數(shù)程序都需要說明和使用變量。廣義講,對象包含了變量,即將變量也稱為一種對象,狹義講,將對象看作是類的實(shí)例,對象是指某個(gè)類的對象。   6 其他,除了以上講述的5個(gè)部分以外,還有其他組成部分。例如,符號常量和注釋信息也是程序的一部分。C++中都盡量把常量定義為符號常量,在C++的程序中出現(xiàn)的是符號常量,該符號常量代表著某個(gè)確定的常量值。   C++程序的書寫格式   在編程時(shí)應(yīng)該注意C++的書寫格式,基本原則是:一行一般寫一條語句。短語句可以一行寫多個(gè)。長語句可以一條寫多行。分行原則是不能將一個(gè)單詞分開。用雙引號引用的一個(gè)字符串也最好不分開,如果一定要分開,有的編譯系統(tǒng)要求在行尾加續(xù)行符(“\”)   C++程序的實(shí)現(xiàn)   C++源程序的實(shí)現(xiàn)與其他高級語言源程序?qū)崿F(xiàn)的原理是一樣的。一般都要經(jīng)過編輯、編譯、運(yùn)行。其中最要的是編譯過程,C++是以編譯方式實(shí)現(xiàn)的高級語言。C++程序的實(shí)現(xiàn),必須要使用某種C++語言的編譯器對程序進(jìn)行編譯。編譯器的功能是將程序的源代碼轉(zhuǎn)換成為機(jī)器代碼的形式,稱為目標(biāo)代碼;然后,再使目標(biāo)代碼進(jìn)行連接,生成可執(zhí)行文件。該過程可分為三個(gè)子過程:預(yù)處理過程、編譯過程(詞法分析、語法分析、符號表、錯(cuò)誤處理程序、生成目標(biāo)代碼)、連接過程。 C++常類型(const) 常類型是指使用類型修飾符const說明的類型,常類型的變量或?qū)ο蟮闹凳遣荒鼙桓碌摹R虼耍x或說明常類型時(shí)必須進(jìn)行初始化。   一般常量和對象常量   1. 一般常量   一般常量是指簡單類型的常量。這種常量在定義時(shí),修飾符const可以用在類型說明符前,也可以用在類型說明符后。如:   int const x=2;   或   const int x=2;   定義或說明一個(gè)常數(shù)組可采用如下格式:   <類型說明符> const <數(shù)組名>[<大小>]…   或者   const <類型說明符> <數(shù)組名>[<大小>]…   例如:   int const a[5]={1, 2, 3, 4, 5};   2. 常對象   常對象是指對象常量,定義格式如下:   <類名> const <對象名>   或者   const <類名> <對象名>   定義常對象時(shí),同樣要進(jìn)行初始化,并且該對象不能再被更新,修飾符const可以放在類名后面,也可以放在類名前面。   常指針和常引用   1. 常指針   使用const修飾指針時(shí),由于const的位置不同,而含意不同。下面舉兩個(gè)例子,說明它們的區(qū)別。   下面定義的一個(gè)指向字符串的常量指針:   char * const prt1 = stringprt1;   其中,ptr1是一個(gè)常量指針。因此,下面賦值是非法的。   ptr1 = stringprt2;   而下面的賦值是合法的:   *ptr1 = "m";   因?yàn)橹羔榩tr1所指向的變量是可以更新的,不可更新的是常量指針ptr1所指的方向(別的字符串)。   下面定義了一個(gè)指向字符串常量的指針:   const * ptr2 = stringprt1;   其中,ptr2是一個(gè)指向字符串常量的指針。ptr2所指向的字符串不能更新的,而ptr2是可以更新的。因此,   *ptr2 = "x";   是非法的,而:   ptr2 = stringptr2;   是合法的。   所以,在使用const修飾指針時(shí),應(yīng)該注意const的位置。定義一個(gè)指向字符串的指針常量和定義一個(gè)指向字符串常量的指針時(shí),const修飾符的位置不同,前者const放在*和指針名之間,后者const放在類型說明符前。   2. 常引用   使用const修飾符也可以說明引用,被說明的引用為常引用,該引用所引用的對象不能被更新。其定義格式如下:   const <類型說明符> & <引用名>   例如:   const double & v;   在實(shí)際應(yīng)用中,常指針和常引用往往用來作函數(shù)的形參,這樣的參數(shù)稱為常參數(shù)。   在C++面向?qū)ο蟮某绦蛟O(shè)計(jì)中,指針和引用使用得較多,其中使用const修飾的常指針和常引用用得更多。使用常參數(shù)則表明該函數(shù)不會更新某個(gè)參數(shù)所指向或所引用的對象,這樣,在參數(shù)傳遞過程中就不需要執(zhí)行拷貝初始化構(gòu)造函數(shù),這將會改善程序的運(yùn)行效率。   下面舉一例子說明常指針作函數(shù)參數(shù)的作法。 #include const int N = 6; void print(const int *p, int n); void main() { int array[N]; for (int i=0; i cin>>array[i]; print(array, N); } void print(const int *p, int n) { cout<<"{"<<*p; for (int i=1; i cout<<","<<*(p+i); cout<<"}"< }   常成員函數(shù)   使用const關(guān)鍵字進(jìn)行說明的成員函數(shù),稱為常成員函數(shù)。只有常成員函數(shù)才有資格操作常量或常對象,沒有使用const關(guān)鍵字說明的成員函數(shù)不能用來操作常對象。常成員函數(shù)說明格式如下:   <類型說明符> <函數(shù)名> (<參數(shù)表>) const; 其中,const是加在函數(shù)說明后面的類型修飾符,它是函數(shù)類型的一個(gè)組成部分,因此,在函數(shù)實(shí)現(xiàn)部分也要帶const關(guān)鍵字。下面舉一例子說明常成員函數(shù)的特征。 #include class R { public: R(int r1, int r2) { R1=r1; R2=r2; } void print(); void print() const; private: int R1, R2; }; void R::print() { cout< } void R::print() const { cout< } void main() { R a(5, 4); a.print(); const R b(20, 52); b.print(); }   該例子的輸出結(jié)果為:   5,4   20;52   該程序的類聲明了兩個(gè)成員函數(shù),其類型是不同的(其實(shí)就是重載成員函數(shù))。有帶const修飾符的成員函數(shù)處理const常量,這也體現(xiàn)出函數(shù)重載的特點(diǎn)。   常數(shù)據(jù)成員   類型修飾符const不僅可以說明成員函數(shù),也可以說明數(shù)據(jù)成員。   由于const類型對象必須被初始化,并且不能更新,因此,在類中說明了const數(shù)據(jù)成員時(shí),只能通過成員初始化列表的方式來生成構(gòu)造函數(shù)對數(shù)據(jù)成員初始化。   下面通過一個(gè)例子講述使用成員初始化列表來生成構(gòu)造函數(shù)。 #include class A { public: A(int i); void print(); const int &r; private: const int a; static const int b; }; const int A::b=10; A::A(int i):a(i), r(a) { } void A::print() { cout< } void main() { A a1(100), a2(0); a1.print(); a2.print(); }   該程序的運(yùn)行結(jié)果為:   100:10:100    0:10:0   在該程序中,說明了如下三個(gè)常類型數(shù)據(jù)成員:   const int & r;   const int a;   static const int b;   其中,r是常int型引用,a是常int型變量,b是靜態(tài)常int型變量。   程序中對靜態(tài)數(shù)據(jù)成員b進(jìn)行初始化。   值得注意的是構(gòu)造函數(shù)的格式如下所示:   A(int i):a(i),r(a)   {   }   其中,冒號后邊是一個(gè)數(shù)據(jù)成員初始化列表,它包含兩個(gè)初始化項(xiàng),用逗號進(jìn)行了分隔,因?yàn)閿?shù)據(jù)成員a和r都是常類型的,需要采用初始化格式。 淺談C++函數(shù)的參數(shù) 函數(shù)參數(shù)的求值順序   當(dāng)一個(gè)函數(shù)帶有多個(gè)參數(shù)時(shí),C++語言沒有規(guī)定在函數(shù)調(diào)用時(shí)實(shí)參的求值順序。而編譯器根據(jù)對代碼進(jìn)行優(yōu)化的需要自行規(guī)定對實(shí)參的求值順序。有的編譯器規(guī)定自左至右,有的編譯器規(guī)定自右至左,這種對求值順序的不同規(guī)定,對一般參數(shù)來講沒有影響。但是,如果實(shí)參表達(dá)式中帶有副作用的運(yùn)算符時(shí),就有可能產(chǎn)生由于求值順序不同而造成了二義性。例如:int z = add_int(++x, x+y);,這樣,在不同的編譯器就有可能生產(chǎn)不同的結(jié)果。   設(shè)置參數(shù)的默認(rèn)值   在C++語言中,允許在函數(shù)的說明或定義時(shí)給一個(gè)或多個(gè)參數(shù)指定默認(rèn)值。但是,要求在一個(gè)指定了默認(rèn)值的參數(shù)的右邊,不能出現(xiàn)沒有指定默認(rèn)值的參數(shù)。例如:    int add_int(int x, int 10);   在上述對函數(shù)add_int()的說明中,對該函數(shù)的最右邊的一個(gè)參數(shù)指定了默認(rèn)值。   在函數(shù)調(diào)用時(shí),編譯器按從左至右的順序?qū)?shí)參與形參結(jié)合,當(dāng)實(shí)參的數(shù)目不足時(shí),編譯器將按同樣的順序用說明中或定義中的默認(rèn)值來補(bǔ)足所缺少的實(shí)參。例如,如有下列的函數(shù)調(diào)用表達(dá)式:    add_int(15)   它將與下列調(diào)用表達(dá)式:    add_int(15, 10)   是等價(jià)的。   在給某個(gè)參數(shù)指定默認(rèn)值是,不僅可以是一個(gè)數(shù)值,而且還可以是任意復(fù)雜的表達(dá)式。   使用數(shù)組作函數(shù)參數(shù)     數(shù)組作函數(shù)參數(shù)可以分為如下三種情況:(這三種情況的結(jié)果相同,只是所采用的調(diào)用機(jī)制不同)   1. 形參和實(shí)參都用數(shù)組   調(diào)用函數(shù)的實(shí)參用數(shù)組名,被調(diào)用函數(shù)的形參用數(shù)組,這種調(diào)用的機(jī)制是形參和實(shí)參共用內(nèi)存中的同一個(gè)數(shù)組。因此,在被調(diào)用函數(shù)中改變了數(shù)組中某個(gè)無素的值,對調(diào)用函數(shù)該數(shù)組的該元素值也被改變,因?yàn)樗鼈兪枪灿猛粋€(gè)數(shù)組。   2. 形參和實(shí)參都用對應(yīng)數(shù)組的指針   在C++中,數(shù)組名被規(guī)定為是一個(gè)指針,該指針便是指向該數(shù)組的首元素的指針,國為它的值是該數(shù)組首元素的地址值,因此,數(shù)組名是一個(gè)常量指針。   實(shí)際中,形參和實(shí)參一個(gè)用指針,另一個(gè)用數(shù)組也是可以的。在使用指針時(shí)可以用數(shù)組名,也可以用另外定義的指向數(shù)組的指針。   3. 實(shí)參用數(shù)組名形參用引用   如何對數(shù)組類型使用引用方式,這里先做如下說明:先用類型定義語句定義一個(gè)int型的數(shù)組類型,如下所示:   typedef int array[8];   然后,使用array來定義數(shù)組和引用。   示例: #include typedef int array[8]; int a[8] = {1, 3, 5, 7, 9, 11, 13}; void fun(array &b, int n) { for(int i=0; i b[7]+=b[i]; } void main() { int m=8; fun(a, m); cout< }   該程序中,在fun()函數(shù)中,使用了引用作形參,調(diào)用時(shí)所對應(yīng)的實(shí)參應(yīng)該是一個(gè)數(shù)組名,這里的引用是給數(shù)組起個(gè)別名。在fun()函數(shù)中對數(shù)組b的操作,就相當(dāng)于b所引用數(shù)組a的操作。在C++中,常用這種調(diào)用方式。 C++語法之函數(shù)重載 所謂函數(shù)重載是指同一個(gè)函數(shù)名可以對應(yīng)著多個(gè)函數(shù)的實(shí)現(xiàn)。例如,可以給函數(shù)名add()定義多個(gè)函數(shù)實(shí)現(xiàn),該函數(shù)的功能是求和,即求兩個(gè)操作數(shù)的和。其中,一個(gè)函數(shù)實(shí)現(xiàn)是求兩個(gè)int型數(shù)之和,另一個(gè)實(shí)現(xiàn)是求兩個(gè)浮點(diǎn)型數(shù)之和,再一個(gè)實(shí)現(xiàn)是求兩個(gè)復(fù)數(shù)的和。每種實(shí)現(xiàn)對應(yīng)著一個(gè)函數(shù)體,這些函數(shù)的名字相同,但是函數(shù)的參數(shù)的類型不同。這就是函數(shù)重載的概念。函數(shù)重載在類和對象的應(yīng)用尤其重要。   函數(shù)重載要求編譯器能夠唯一地確定調(diào)用一個(gè)函數(shù)時(shí)應(yīng)執(zhí)行哪個(gè)函數(shù)代碼,即采用哪個(gè)函數(shù)實(shí)現(xiàn)。確定函數(shù)實(shí)現(xiàn)時(shí),要求從函數(shù)參數(shù)的個(gè)數(shù)和類型上來區(qū)分。這就是說,進(jìn)行函數(shù)重載時(shí),要求同名函數(shù)在參數(shù)個(gè)數(shù)上不同,或者參數(shù)類型上不同。否則,將無法實(shí)現(xiàn)重載。   參數(shù)類型上不同的重載函數(shù)   下面舉一個(gè)在參數(shù)類型不同的重載函數(shù)的例子: #include int add(int, int); double add(double, double); void main() { cout< cout< } int add(int x, int y) { return x+y; } double add(double a, double b) { return a+b; }   該程序中,main()函數(shù)中調(diào)用相同名字add的兩個(gè)函數(shù),前邊一個(gè)add()函數(shù)對應(yīng)的是兩個(gè)int型數(shù)求和的函數(shù)實(shí)現(xiàn),而后邊一個(gè)add()函數(shù)對應(yīng)的是兩個(gè)double型數(shù)求和的函數(shù)實(shí)現(xiàn)。這便是函數(shù)的重載。   以上程序輸出結(jié)果為:   15   15.5   參數(shù)個(gè)數(shù)上不同的重載函數(shù)   下面舉一個(gè)在參數(shù)個(gè)數(shù)上不相同的重載函數(shù)的例子: #include int min(int a, int b); int min(int a, int b, int c); int min(int a, int b, int c, int d); void main() { cout< cout< } int min(int a, int b) { return a } int min(int a, int b, int c) { int t = min(a, b); return min(t,c); } int min(int a, int b, int c, int d) { int t1 = min(a, b); int t2 = min(c, d); return min(t1, t2); }   該程序中出現(xiàn)了函數(shù)重載,函數(shù)名min對應(yīng)有三個(gè)不同的實(shí)現(xiàn),函數(shù)的區(qū)分依據(jù)參數(shù)個(gè)數(shù)不同,這里的三個(gè)函數(shù)實(shí)現(xiàn)中,參數(shù)個(gè)數(shù)分別為2,3和4,在調(diào)用函數(shù)時(shí)根據(jù)實(shí)參的個(gè)數(shù)來選取不同的函數(shù)實(shí)現(xiàn)。   函數(shù)重載在類和對象應(yīng)用比較多,尤其是在類的多態(tài)性中。在以后我們將碰到更多的在類型不同的函數(shù)重載,尤其是在結(jié)合類的繼承性和指針類型的不同,而這些都是我們以后用VC編程中經(jīng)常要用到的。 C++子對象和堆對象 子對象   當(dāng)一個(gè)類的成員是某一個(gè)類的對象時(shí),該對象就為子對象。子對象實(shí)際就是對象成員。如: class A {  public:   …  private:   … }; class B  {   public:    …   private:    A a;    …  };   其中,B類中成員a就是子對象,它是A類的對象作為B類的成員。   在類中出現(xiàn)了子對象或稱對象成員時(shí),該類的構(gòu)造函數(shù)要包含對子對象的初始化,通常采用成員初始化表的方法來初始化子對象。在成員初始化表中包含對子對象的初始化和對類中其他成員的初始化。下面舉一例子說明成員初始化的構(gòu)造。 #include class A { public: A(int i, int j) { A1=i; A2=j; } void print() { cout< private: int A1, A2; }; class B { public: B(int i, int j, int k):a(i, j), b(k) { } void print(); private: A a; file://子對象 int b; }; void B::print() { a.print(); cout< } void main() { B b(6, 7, 8); b.print(); }   該程序的輸出結(jié)果為:   6,7   8   其中,a(i, j), b(k)是成員初始化表,它有二項(xiàng),前一項(xiàng)是給子對象a初始化,其格式如下:    <子對象名> (<參數(shù)表>)   后一項(xiàng)是給類B的數(shù)據(jù)成員b初始化。這一項(xiàng)也可以寫在構(gòu)造函數(shù)的函數(shù)體內(nèi),使用賦值表達(dá)式語句    b = k;   給類B的數(shù)據(jù)成員初始化。   堆對象   所謂堆對象是指在程序運(yùn)行過程中根據(jù)需要隨時(shí)可以建立或刪除的對象。這種堆對象被創(chuàng)建在內(nèi)存一些空閑的存儲單元中,這些存儲單元被稱為堆。它們可以被創(chuàng)建的堆對象占有,也可以通過刪除堆對象而獲得釋放。   創(chuàng)建或刪除堆對象時(shí),需要如下兩個(gè)運(yùn)算符:    new    delete   這兩個(gè)運(yùn)算符又稱為動態(tài)分配內(nèi)存空間運(yùn)算符。new相當(dāng)于C語言中malloc()函數(shù),而delete相當(dāng)于C語言中free()函數(shù)。   1. 運(yùn)算符new的用法   該運(yùn)算符的功能是用來創(chuàng)建堆對象,或者說,它是用來動態(tài)地創(chuàng)建對象。   new運(yùn)算符使用格式如下:   new <類型說明符> (<初始值列表>)   它表明在堆中建立一個(gè)由<類型說明符>給定的類型的對象,并且由括號中的<初始值列表>給出被創(chuàng)建對象的初始值。如果省去括號和括號中的初始值,則被創(chuàng)建的對象選用缺省值。   使用new運(yùn)算符創(chuàng)建對象時(shí),它可以根據(jù)其參數(shù)來選擇適當(dāng)?shù)臉?gòu)造函數(shù),它不用sizeof來計(jì)算對象所占的字節(jié)數(shù),而可以計(jì)算其大小。   new運(yùn)算符返回一個(gè)指針,指針類型將與new所分配對象相匹配,如果不匹配可以通過強(qiáng)制類型的方法,否則將出現(xiàn)編譯錯(cuò)。   如果new運(yùn)算符不能分配到所需要的內(nèi)存,它將返回0,這時(shí)的指針為空指針。   運(yùn)算符new也可以用來創(chuàng)建數(shù)組類型的對象,即對象數(shù)組。其格式如下:    new <類名> [<算術(shù)表達(dá)式>]   其中,<算術(shù)表達(dá)式>的值為所創(chuàng)建的對象數(shù)組的大小。如:    A *ptr;    ptr = new A[5];   new還可用來創(chuàng)建一般類型的數(shù)組。如:    int *p;    p = new int[10];   使用new[]創(chuàng)建的對象數(shù)組或一般數(shù)組時(shí),不能為該數(shù)組指定初始值,其初始值為缺省值。   2. 運(yùn)算符delete的用法   該運(yùn)算符的功能是用來刪除使用new創(chuàng)建的對象或一般類型的指針。其格式如下:    delete <指針名>   例如:    A *ptr;    ptr = new A(5, 6);    delete ptr;   運(yùn)算符delete也可用來刪除使用new創(chuàng)建對象數(shù)組,其使用格式如下:    delete[] <指針名>   同樣,delete也可以刪除由new創(chuàng)建的一般類型的數(shù)組。如:    int *p;    p = new int[10];    delete[] p;   使用運(yùn)算符delete時(shí),應(yīng)注意如下幾點(diǎn):    (1) 它必須使用于由運(yùn)算符new返回的指針;    (2) 該運(yùn)算符也適用于空指針(即其值為0的指針);    (3) 指針名前只用一對方括號符,并且不管所刪除數(shù)組的維數(shù),忽略方括號內(nèi)的任何數(shù)字。   下面舉一例子說明new運(yùn)算符和delete運(yùn)算符的使用方法。 #include class AA { public: AA(int i, int j) { A=i; B=j; cout<<"構(gòu)造函數(shù).\n"; } ~AA() { cout<<"析構(gòu)函數(shù).\n"; } void print(); private: int A, B; }; void AA::print() { cout< } void main() { AA *a1, *a2; a1 = new AA(1, 2); a2 = new AA(5, 6); a1->print(); a2->print(); delete a1; delete a2; }    該程序的輸出結(jié)果為:     構(gòu)造函數(shù).     構(gòu)造函數(shù).     1, 2     5, 6     構(gòu)造函數(shù).     構(gòu)造函數(shù).   從程序中可以看到:用new創(chuàng)建對象時(shí),要調(diào)用構(gòu)造函數(shù),用delete刪除對象時(shí),要調(diào)用析構(gòu)函數(shù)。如果創(chuàng)建或刪除的時(shí)對象數(shù)組,對象數(shù)組有多少,就調(diào)用多少次構(gòu)造函數(shù)或構(gòu)造函數(shù)。   在實(shí)際應(yīng)用中,經(jīng)常對于new運(yùn)算符返回的指針進(jìn)行檢驗(yàn),看是否分配了有效的內(nèi)存空間。結(jié)合本例給出檢驗(yàn)方法如下:   if (!a1)    {     cout<<"Heap erroe!\n";     exit(1);    }   下面再舉一個(gè)使用new和delete運(yùn)算符對一般指針和數(shù)組的例子。 #include #include void fun() { int *p; if (p = new int) { *p = 5; cout<<*p< delete p; } else cout<<"Heap error!\n"; } void main() { fun(); int *pa; pa = new int[5]; if (!pa) { cout<<"Heap error!\n"; exit(1); } for (int i=0; i<5; i++) pa[i] = i+1; for (i=0; i<5; i++) cout<(<派生類構(gòu)造函數(shù)總參數(shù)表>):<基類構(gòu)造函數(shù)>(參數(shù)表1),<子對象名>(<參數(shù)表2>)   {   <派生類中數(shù)據(jù)成員初始化>   };   派生類構(gòu)造函數(shù)的調(diào)用順序如下:    · 基類的構(gòu)造函數(shù)    · 子對象類的構(gòu)造函數(shù)(如果有的話)    · 派生類構(gòu)造函數(shù)   在前面的例子中,B::B(int i, int j, int k):A(i), bb(j), bbb(k)就是派生類構(gòu)造函數(shù)的定義,下面再舉一個(gè)構(gòu)造派生類構(gòu)造函數(shù)的例子。 #include class A { public: A() { a=0; cout<<"類A的缺省構(gòu)造函數(shù).\n"; } A(int i) { a=i; cout<<"類A的構(gòu)造函數(shù).\n"; } ~A() { cout<<"類A的析構(gòu)函數(shù).\n"; } void Print() const { cout< int Geta() { reutrn a; } private: int a; } class B : public A { public: B() { b=0; cout<<"類B的缺省構(gòu)造函數(shù).\n"; } B(int i, int j, int k); ~B() { cout<<"類B的析構(gòu)函數(shù).\n"; } void Print(); private: int b; A aa; } B::B(int i, int j, int k):A(i), aa(j) { b=k; cout<<"類B的構(gòu)造函數(shù).\n"; } void B::Print() { A::Print(); cout< } void main() { B bb[2]; bb[0] = B(1, 2, 5); bb[1] = B(3, 4, 7); for(int i=0; i<2; i++) bb[i].Print(); }   2. 構(gòu)造函數(shù)   當(dāng)對象被刪除時(shí),派生類的析構(gòu)函數(shù)被執(zhí)行。由于析構(gòu)函數(shù)也不能被繼承,因此在執(zhí)行派生類的析構(gòu)函數(shù)時(shí),基類的析構(gòu)函數(shù)也將被調(diào)用。執(zhí)行順序是先執(zhí)行派生類的構(gòu)造函數(shù),再執(zhí)行基類的析構(gòu)函數(shù),其順序與執(zhí)行構(gòu)造函數(shù)時(shí)的順序正好相反。這一點(diǎn)從前面講過的例子可以看出,請讀者自行分析。   3. 派生類構(gòu)造函數(shù)使用中應(yīng)注意的問題   (1) 派生類構(gòu)造函數(shù)的定義中可以省略對基類構(gòu)造函數(shù)的調(diào)用,其條件是在基類中必須有缺省的構(gòu)造函數(shù)或者根本沒有定義構(gòu)造函數(shù)。當(dāng)然,基類中沒有定義構(gòu)造函數(shù),派生類根本不必負(fù)責(zé)調(diào)用基類的析構(gòu)函數(shù)。   (2) 當(dāng)基類的構(gòu)造函數(shù)使用一個(gè)或多個(gè)參數(shù)時(shí),則派生類必須定義構(gòu)造函數(shù),提供將參數(shù)傳遞給基類構(gòu)造函數(shù)途徑。在有的情況下,派生類構(gòu)造函數(shù)的函數(shù)體可能為空,僅起到參數(shù)傳遞作用。如本講第一個(gè)例子就屬此種情況。   子類型化和類型適應(yīng)   1. 子類型化   子類型的概念涉及到行為共享,它與繼承有著密切關(guān)系。   有一個(gè)特定的類型S,當(dāng)且僅當(dāng)它至少提供了類型T的行為,由稱類型S是類型T的子類型。子類型是類型之間的一般和特殊的關(guān)系。   在繼承中,公有繼承可以實(shí)現(xiàn)子類型。例如: class A { public: void Print() const { cout<<"A::print() called.\n"; } }; class B : public A { public: void f() {} };   類B繼承了類A,并且是公有繼承方式。因此,可以說類B是類A的一個(gè)子類型。類A還可以有其他的子類型。類B是類A的子類型,類B具備類A中的操作,或者說類A中的操作可被用于操作類B的對象。   子類型關(guān)系是不可逆的。這就是說,已知B是A的子類型,而認(rèn)為A也是B的子類型是錯(cuò)誤的,或者說,子類型關(guān)系是不對稱不。   因此,可以說公有繼承可以實(shí)現(xiàn)子類型化。   2. 類型適應(yīng)   類型適應(yīng)是指兩種類型之間的關(guān)系。例如,B類型適應(yīng)A類型是指B類型的對象能夠用于A類型的對象所能使用的場合。   前面講過的派生類的對象可以用于基類對象所能使用的場合,我們說派生類適應(yīng)于基類。   同樣道理,派生類對象的指針和引用也適應(yīng)于基類對象的指針和引用。   子類型化與類型適應(yīng)是致的。A類型是B類型的子類型,那么A類型必將適應(yīng)于B類型。   子類型的重要性就在于減輕程序人員編寫程序代碼的負(fù)擔(dān)。因?yàn)橐粋€(gè)函數(shù)可以用于某類型的對象,則它也可以用于該類型的各個(gè)子類型的對象,這樣就不必為處理這些子類型的對象去重載該函數(shù)。 C++多繼承   多繼承可以看作是單繼承的擴(kuò)展。所謂多繼承是指派生類具有多個(gè)基類,派生類與每個(gè)基類之間的關(guān)系仍可看作是一個(gè)單繼承。   多繼承下派生類的定義格式如下:   class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>,…    {     <派生類類體>    };   其中,<繼承方式1>,<繼承方式2>,…是三種繼承方式:public、private、protected之一。例如: class A { … }; class B { … }; class C : public A, public, B { … }; 其中,派生類C具有兩個(gè)基類(類A和類B),因此,類C是多繼承的。按照繼承的規(guī)定,派生類C的成員包含了基類B中成員以及該類本身的成員。   多繼承的構(gòu)造函數(shù)   在多繼承的情況下,派生類的構(gòu)造函數(shù)格式如下:   <派生類名>(<總參數(shù)表>):<基類名1>(<參數(shù)表1>),<基類名2>(<參數(shù)表2>),…    <子對象名>(<參數(shù)表n+1>),…     {      <派生類構(gòu)造函數(shù)體>     }   其中,<總參數(shù)表>中各個(gè)參數(shù)包含了其后的各個(gè)分參數(shù)表。   多繼承下派生類的構(gòu)造函數(shù)與單繼承下派生類構(gòu)造函數(shù)相似,它必須同時(shí)負(fù)責(zé)該派生類所有基類構(gòu)造函數(shù)的調(diào)用。同時(shí),派生類的參數(shù)個(gè)數(shù)必須包含完成所有基類初始化所需的參數(shù)個(gè)數(shù)。   派生類構(gòu)造函數(shù)執(zhí)行順序是先執(zhí)行所胡基類的構(gòu)造函數(shù),再執(zhí)行派生類本身構(gòu)造函數(shù),處于同一層次的各基類構(gòu)造函數(shù)的執(zhí)行順序取決于定義派生類時(shí)所指定的各基類順序,與派生類構(gòu)造函數(shù)中所定義的成員初始化列表的各項(xiàng)順序無關(guān)。也就是說,執(zhí)行基類構(gòu)造函數(shù)的順序取決于定義派生類時(shí)基類的順序。可見,派生類構(gòu)造函數(shù)的成員初始化列表中各項(xiàng)順序可以任意地排列。     下面通過一個(gè)例子來說明派生類構(gòu)造函數(shù)的構(gòu)成及其執(zhí)行順序。 #include class B1 { public: B1(int i) { b1 = i; cout<<"構(gòu)造函數(shù) B1."< } void print() { cout< private: int b1; }; class B2 { public: B2(int i) { b2 = i; cout<<"構(gòu)造函數(shù) B2."< } void print() { cout< private: int b2; }; class B3 { public: B3(int i) { b3 = i; cout<<"構(gòu)造函數(shù) B3."< } int getb3() { return b3; } private: int b3; }; class A : public B2, public B1 { public: A(int i, int j, int k, int l):B1(i), B2(j), bb(k) { a = l; cout<<"構(gòu)造函數(shù) A."< } void print() { B1::print(); B2::print(); cout< } private: int a; B3 bb; }; void main() { A aa(1, 2, 3, 4); aa.print(); }   該程序的輸出結(jié)果為:    構(gòu)造函數(shù) B2.2    構(gòu)造函數(shù) B1.1    構(gòu)造函數(shù) B3.3    構(gòu)造函數(shù) A.4    1    2    4, 3   在該程序中,作用域運(yùn)算符::用于解決作用域沖突的問題。在派生類A中的print()函數(shù)的定義中,使用了B1::print;和B2::print();語句分別指明調(diào)用哪一個(gè)類中的print()函數(shù),這種用法應(yīng)該學(xué)會。 C++多繼承 8/27/2001 8:37:55· ·--··pcvc 上一頁 1 2    二義性問題   一般說來,在派生類中對基類成員的訪問應(yīng)該是唯一的,但是,由于多繼承情況下,可能造成對基類中某成員的訪問出現(xiàn)了不唯一的情況,則稱為對基類成員訪問的二義性問題。   實(shí)際上,在上例已經(jīng)出現(xiàn)過這一問題,回憶一下上例中,派生類A的兩基類B1和B2中都有一個(gè)成員函數(shù)print()。如果在派生類中訪問print()函數(shù),到底是哪一個(gè)基類的呢?于是出現(xiàn)了二義性。但是在上例中解決了這個(gè)問題,其辦法是通過作用域運(yùn)算符::進(jìn)行了限定。如果不加以限定,則會出現(xiàn)二義性問題。   下面再舉一個(gè)簡單的例子,對二義性問題進(jìn)行深入討論。例如: class A { public: void f(); }; class B { public: void f(); void g(); }; class C : public A, public B { public: void g(); void h(); };   如果定義一個(gè)類C的對象c1:    C c1;   則對函數(shù)f()的訪問    c1.f();   便具有二義性:是訪問類A中的f(),還是訪問類B中的f()呢?   解決的方法可用前面用過的成員名限定法來消除二義性,例如:    c1.A::f();   或者    c1.B::f();   但是,最好的解決辦法是在類C中定義一個(gè)同名成員f(),類C中的f()再根據(jù)需要來決定調(diào)用A::f(),還是B::f(),還是兩者皆有,這樣,c1.f()將調(diào)用C::f()。   同樣地,類C中成員函數(shù)調(diào)用f()也會出現(xiàn)二義性問題。例如:   viod C::h()    {     f();    }   這里有二義性問題,該函數(shù)應(yīng)修改為:    void C::h()    {     A::f();    }   或者    void C::h()    {     B::f();    }   或者    void C::f()    {     A::f();     B::f();    }   另外,在前例中,類B中有一個(gè)成員函數(shù)g(),類C中也有一個(gè)成員函數(shù)g()。這時(shí),    c1.g();   不存在二義性,它是指C::g(),而不是指B::g()。因?yàn)檫@兩個(gè)g()函數(shù),一個(gè)出現(xiàn)在基類B,一個(gè)出現(xiàn)在派生類C,規(guī)定派生類的成員將支配基類中的同名成員。因此,上例中類C中的g()支配類B中的g(),不存在二義性,可選擇支配者的那個(gè)名字。   當(dāng)一個(gè)派生類從多個(gè)基類派生類,而這些基類又有一個(gè)共同的基類,則對該基類中說明的成員進(jìn)行訪問時(shí),也可能會出現(xiàn)二義性。例如: class A { public: int a; }; class B1 : public A { private: int b1; }; class B2 : public A { private: int b2; }; class C : public B1, public B2 { public: int f(); private: int c; };   已知:C c1;   下面的兩個(gè)訪問都有二義性:   c1.a;   c1.A::a;   而下面的兩個(gè)訪問是正確的:   c1.B1::a;   c1.B2::a;   類C的成員函數(shù)f()用如下定義可以消除二義性:   int C::f()    {     retrun B1::a + B2::a;    }   由于二義性的原因,一個(gè)類不可以從同一個(gè)類中直接繼承一次以上,例如:   class A : public B, public B    {     …    }   這是錯(cuò)誤的。 C++ 對象與數(shù)組 對象數(shù)組是指數(shù)組元素為對象的數(shù)組。該數(shù)組中若干個(gè)元素必須是同一個(gè)類的若干個(gè)對象。對象數(shù)組的定義、賦值和引用與普通數(shù)組一樣,只是數(shù)組的元素與普通數(shù)組不同,它是同類的若干個(gè)對象。   1. 對象數(shù)組的定義   對象數(shù)組定義格式如下:    <類名><數(shù)組名>[<大小>]...   其中,<類名>指出該數(shù)組元素是屬于該類的對象,方括號內(nèi)的<大小>給出某一維的元素個(gè)數(shù)。一維對象數(shù)組只有一個(gè)方括號,二維對象數(shù)組要有兩個(gè)方括號,等等,例如:    DATE dates[7];   表明dates是一維對象數(shù)組名,該數(shù)組有7個(gè)元素,每個(gè)元素都是類DATE的對象。   2. 對象數(shù)組的賦值   對象數(shù)組可以被賦初值,也可以被賦值。例如: class DATE {  public:   DATE(int m, int d, int y);   void printf();  private:   int month, day, year; };   下面是定義對象數(shù)組并賦初值和賦值:    DATE dates[4]={ DATE(7, 7, 2001), DATE(7, 8, 2001), DATE(7, 9, 2001), DATE(7, 10, 2001) }   或者    dates[0] = DATE(7, 7, 2001);    dates[1] = DATE(7, 8, 2001);    dates[2] = DATE(7, 9, 2001);    dates[3] = DATE(7, 10, 2001); 指向數(shù)組的指針和指針數(shù)組   指向數(shù)組的指針和指針數(shù)組是兩個(gè)完全不同的概念,現(xiàn)放在一起介紹是中為兩者在定義格式相似,千萬不要把它們搞混了。   1. 指向數(shù)組的指針   指向一般數(shù)組的指針定義格式如下:    <類型說明符>(*<指針名>)[<大小>]...   其中,用來說明指針的 * 要與<指針名>括在一起。后面用一個(gè)方括號表示該指針指向一維數(shù)組,后面用二個(gè)方括號表示該指針指向二維數(shù)組。<類型說明符>用來說明指針?biāo)赶虻臄?shù)組的元素的類型。例如:    int (*P)[3];   P是一個(gè)指向一維數(shù)組的指針,該數(shù)組有3個(gè)int型元素。   而指向?qū)ο髷?shù)組的指針,則把<類型說明符>改為<類名>即可:    <類名>(*<指針名>)[<大小>]...   指向數(shù)組的指針的主要應(yīng)用思想是:將數(shù)組的首地址(二維數(shù)組的某個(gè)行地址)賦給指針,然后通過循環(huán)(for)改變指針指向的地址,從而動態(tài)的訪問數(shù)組中各個(gè)元素。   2. 指針數(shù)組   所謂指針數(shù)組指的是數(shù)組元素為指針的那類數(shù)組。一個(gè)數(shù)組的元素可以是指向同一類型的一般指針,也可以是指向同一類類型的對象。   一般指針數(shù)組的定義格式如下:    <類型名>*<數(shù)組名>[<大小>]...   其中,*加在<數(shù)組名>前面表示該數(shù)組為指針數(shù)組。[<大小>]表示某一維的大小,即該維的元素個(gè)數(shù),…表示可以是多維指針數(shù)組,每一個(gè)[<大小>]表示一維。例如:    int * pa[3];    char * pc[2][5];   在C++編程中,經(jīng)常使用char型的指針數(shù)組用來存放若干個(gè)字符串。下面是一個(gè)一維指針數(shù)組的例子。 #include #include const int N = 5; void main() { char *strings[N]; file://定義一個(gè)一維指針數(shù)組strings char str[80]; cout<<"At each prompt, enter a string:\n"; for (int i=0; i { cout<<"Enter a string #"< cin.getline(str, sizeof(str)); strings[i] = new char[strlen(str) + 1]; strcpy(strings[i], str); } cout< for (i=0; i cout<<"String #"< }   對象指針數(shù)組的定義如下:   對象指針數(shù)組是指該數(shù)組的元素是指向?qū)ο蟮闹羔槪笏袛?shù)組元素都是指向同一個(gè)類類型的對象的指針。格式如下:   <類名>*<數(shù)組名>[<大小>]...   它與前面講過的一般的指針數(shù)組所不同的地方僅在于該數(shù)組一定是指向?qū)ο蟮闹羔槨<粗赶驅(qū)ο蟮闹羔樣脕碜髟摂?shù)組的元素。下面通過一個(gè)例子看一下對象指針數(shù)組的用法。 #include class A { public: A(int i=0, int j=0) { a=i; b=j; } void print(); private: int a, b; }; void A::print() { cout< } void main() { A a1(7, 8), a2, a3(5, 7); A *b[3] = { &a3, &a2, &a1 }; for (int i=0; i<3; i++) b[i]->print(); }   帶參數(shù)的main()參數(shù)   前面講過的main()函數(shù)都是不帶參數(shù)的。在實(shí)際編程中,有時(shí)需要main()帶參數(shù)。通過main()函數(shù)的參數(shù)給程序增加一些處理信息。一般地說,當(dāng)使用C++編寫的源程序經(jīng)過編譯連接生成的可執(zhí)行文件在執(zhí)行時(shí),需要還命令行參數(shù),由該源程序的主函數(shù)main()就需要帶參數(shù)。使用所還有的參數(shù)來存放命令行中的參數(shù),以便在程序中對命令行參數(shù)進(jìn)行處理。   帶有參數(shù)的main()函數(shù)頭格式如下:    void main(int argc, char * argv[])   其中,第一個(gè)參數(shù)argc是int型的,它用來存放命令行參數(shù)的個(gè)數(shù),實(shí)際上argc所存放的數(shù)值比命令行參數(shù)的個(gè)數(shù)多1,即將命令字(可執(zhí)行文件名)也計(jì)算在內(nèi)。第二個(gè)參數(shù)argv是一個(gè)一維的一級指針數(shù)組,它是用來存放命令行中各個(gè)參數(shù)和命令字的字符串的,并且規(guī)定:   argv[0]存放命令字   argv[1]存放命令行中第一個(gè)參數(shù)   argv[2]存放命令行中第二個(gè)參數(shù)   …   這里,argc的值和argv[]各元素的值都是系統(tǒng)自動組賦值的。   在這里講述帶參數(shù)的main()函數(shù)實(shí)際上是對指針數(shù)組應(yīng)用的一個(gè)具體實(shí)例。 #include void main(int argc, char *argv[]) { cout<<"The number of command line arguments is:"< cout<<"The program name is:"< if (argc>1) { cout<<"The command line arguments:\n"; for (int i=1; i cout< } }   上述編譯連接后的EXE文件,可在DOS命令行下調(diào)試。   關(guān)于命令行參數(shù)的使用,其基本方法是直接引用指針數(shù)組argv[]中某個(gè)元素所存放的字符串,可用下標(biāo)方式,也可用指針方式。 C++ 類的靜態(tài)成員(static) 靜態(tài)成員的提出是為了解決數(shù)據(jù)共享的問題。實(shí)現(xiàn)共享有許多方法,如:設(shè)置全局性的變量或?qū)ο笫且环N方法。但是,全局變量或?qū)ο笫怯芯窒扌缘摹_@一章里,我們主要講述類的靜態(tài)成員來實(shí)現(xiàn)數(shù)據(jù)的共享。   靜態(tài)數(shù)據(jù)成員   在類中,靜態(tài)成員可以實(shí)現(xiàn)多個(gè)對象之間的數(shù)據(jù)共享,并且使用靜態(tài)數(shù)據(jù)成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態(tài)成員是類的所有對象中共享的成員,而不是某個(gè)對象的成員。   使用靜態(tài)數(shù)據(jù)成員可以節(jié)省內(nèi)存,因?yàn)樗撬袑ο笏械模虼耍瑢Χ鄠€(gè)對象來說,靜態(tài)數(shù)據(jù)成員只存儲一處,供所有對象共用。靜態(tài)數(shù)據(jù)成員的值對每個(gè)對象都是一樣,但它的值是可以更新的。只要對靜態(tài)數(shù)據(jù)成員的值更新一次,保證所有對象存取更新后的相同的值,這樣可以提高時(shí)間效率。   靜態(tài)數(shù)據(jù)成員的使用方法和注意事項(xiàng)如下:   1、靜態(tài)數(shù)據(jù)成員在定義或說明時(shí)前面加關(guān)鍵字static。   2、靜態(tài)成員初始化與一般數(shù)據(jù)成員初始化不同。靜態(tài)數(shù)據(jù)成員初始化的格式如下:     <數(shù)據(jù)類型><類名>::<靜態(tài)數(shù)據(jù)成員名>=<值>   這表明:      (1) 初始化在類體外進(jìn)行,而前面不加static,以免與一般靜態(tài)變量或?qū)ο笙嗷煜?   (2) 初始化時(shí)不加該成員的訪問權(quán)限控制符private,public等。   (3) 初始化時(shí)使用作用域運(yùn)算符來標(biāo)明它所屬類,因此,靜態(tài)數(shù)據(jù)成員是類的成員,而不是對象的成員。   3、靜態(tài)數(shù)據(jù)成員是靜態(tài)存儲的,它是靜態(tài)生存期,必須對它進(jìn)行初始化。   4、引用靜態(tài)數(shù)據(jù)成員時(shí),采用如下格式:    <類名>::<靜態(tài)成員名>   如果靜態(tài)數(shù)據(jù)成員的訪問權(quán)限允許的話(即public的成員),可在程序中,按上述格式來引用靜態(tài)數(shù)據(jù)成員。   下面舉一例子,說明靜態(tài)數(shù)據(jù)成員的應(yīng)用: #include class Myclass { public: Myclass(int a, int b, int c); void GetNumber(); void GetSum(); private: int A, B, C; static int Sum; }; int Myclass::Sum = 0; Myclass::Myclass(int a, int b, int c) { A = a; B = b; C = c; Sum += A+B+C; } void Myclass::GetNumber() { cout<<"Number="< } void Myclass::GetSum() { cout<<"Sum="< } void main() { Myclass M(3, 7, 10),N(14, 9, 11); M.GetNumber(); N.GetNumber(); M.GetSum(); N.GetSum(); }   從輸出結(jié)果可以看到Sum的值對M對象和對N對象都是相等的。這是因?yàn)樵诔跏蓟疢對象時(shí),將M對象的三個(gè)int型數(shù)據(jù)成員的值求和后賦給了Sum,于是Sum保存了該值。在初始化N對象時(shí),對將N對象的三個(gè)int型數(shù)據(jù)成員的值求和后又加到Sum已有的值上,于是Sum將保存另后的值。所以,不論是通過對象M還是通過對象N來引用的值都是一樣的,即為54。   靜態(tài)成員函數(shù)   靜態(tài)成員函數(shù)和靜態(tài)數(shù)據(jù)成員一樣,它們都屬于類的靜態(tài)成員,它們都不是對象成員。因此,對靜態(tài)成員的引用不需要用對象名。   在靜態(tài)成員函數(shù)的實(shí)現(xiàn)中不能直接引用類中說明的非靜態(tài)成員,可以引用類中說明的靜態(tài)成員。如果靜態(tài)成員函數(shù)中要引用非靜態(tài)成員時(shí),可通過對象來引用。下面通過例子來說明這一點(diǎn)。 #include class M { public: M(int a) { A=a; B+=a;} static void f1(M m); private: int A; static int B; }; void M::f1(M m) { cout<<"A="< cout<<"B="< } int M::B=0; void main() { M P(5),Q(10); M::f1(P); file://調(diào)用時(shí)不用對象名 M::f1(Q); }   讀者可以自行分析其結(jié)果。從中可看出,調(diào)用靜態(tài)成員函數(shù)使用如下格式:    <類名>::<靜態(tài)成員函數(shù)名>(<參數(shù)表>); C++ 虛基類 在《多繼承》中講過的例子中,由類A,類B1和類B2以及類C組成了類繼承的層次結(jié)構(gòu)。在該結(jié)構(gòu)中,類C的對象將包含兩個(gè)類A的子對象。由于類A是派生類C兩條繼承路徑上的一個(gè)公共基類,那么這個(gè)公共基類將在派生類的對象中產(chǎn)生多個(gè)基類子對象。如果要想使這個(gè)公共基類在派生類中只產(chǎn)生一個(gè)基類子對象,則必須將這個(gè)基類設(shè)定為虛基類。   虛基類的引入和說明   前面簡單地介紹了要引進(jìn)虛基類的原因。實(shí)際上,引進(jìn)虛基類的真正目的是為了解決二義性問題。   虛基類說明格式如下:    virtual <繼承方式><基類名>   其中,virtual是虛類的關(guān)鍵字。虛基類的說明是用在定義派生類時(shí),寫在派生類名的后面。例如: class A {  public:   void f();   protected:   int a;  }; class B : virtual public A {   protected:   int b;  }; class C : virtual public A {   protected:   int c:  }; class D : public B, public C {   public:   int g();   private:   int d; };   由于使用了虛基類,使得類A,類B,類C和類D之間關(guān)系用DAG圖示法表示如下: A{ f(), a } / \ B{b} C{c} \ / D{g(),d} 從該圖中可見不同繼承路徑的虛基類子對象被合并成為一個(gè)對象。這便是虛基類的作用,這樣將消除了合并之前可能出現(xiàn)的二義性。這時(shí),在類D的對象中只存在一個(gè)類A的對象。因此,下面的引用都是正確的:   D n;   n.f(); file://對f()引用是正確的。  void D::g()  {   f(); file://對f()引用是正確的。  }   下面程序段是正確的。   D n;   A *pa;   pa = &n;   其中,pa是指向類A對象的指針,n是類D的一個(gè)對象,&n是n對象的地址。pa=&n是讓pa指針指向類D的對象,這是正確的,并且也無二義性。 C++ 虛基類 9/3/2001 8:22:51· ·--··pcvc 上一頁 1 2    虛基類的構(gòu)造函數(shù)   前面講過,為了初始化基類的子對象,派生類的構(gòu)造函數(shù)要調(diào)用基類的構(gòu)造函數(shù)。對于虛基類來講,由于派生類的對象中只有一個(gè)虛基類子對象。為保證虛基類子對象只被初始化一次,這個(gè)虛基類構(gòu)造函數(shù)必須只被調(diào)用一次。由于繼承結(jié)構(gòu)的層次可能很深,規(guī)定將在建立對象時(shí)所指定的類稱為最派生類。C++規(guī)定,虛基類子對象是由最派生類的構(gòu)造函數(shù)通過調(diào)用虛基類的構(gòu)造函數(shù)進(jìn)行初始化的。如果一個(gè)派生類有一個(gè)直接或間接的虛基類,那么派生類的構(gòu)造函數(shù)的成員初始列表中必須列出對虛基類構(gòu)造函數(shù)的調(diào)用。如果未被列出,則表示使用該虛基類的缺省構(gòu)造函數(shù)來初始化派生類對象中的虛基類子對象。   從虛基類直接或間接繼承的派生類中的構(gòu)造函數(shù)的成員初始化列表中都要列出這個(gè)虛基類構(gòu)造函數(shù)的調(diào)用。但是,只有用于建立對象的那個(gè)最派生類的構(gòu)造函數(shù)調(diào)用虛基類的構(gòu)造函數(shù),而該派生類的基類中所列出的對這個(gè)虛基類的構(gòu)造函數(shù)調(diào)用在執(zhí)行中被忽略,這樣便保證了對虛基類的對象只初始化一次。   C++又規(guī)定,在一個(gè)成員初始化列表中出現(xiàn)對虛基類和非虛基類構(gòu)造函數(shù)的調(diào)用,則虛基類的構(gòu)造函數(shù)先于非虛基類的構(gòu)造函數(shù)的執(zhí)行。   下面舉一例子說明具有虛基類的派生類的構(gòu)造函數(shù)的用法。 #include class A { public: A(const char *s) { cout< ~A() {} }; class B : virtual public A { public: B(const char *s1, const char *s2):A(s1) { cout< } }; class C : virtual public A { public: C(const char *s1, const char *s2):A(s1) { cout< } }; class D : public B, public C { public: D(const char *s1, const char *s2, const char *s3, const char *s4) :B(s1, s2), C(s1, s3), A(s1) { cout< } }; void main() { D *ptr = new D("class A", "class B", "class C", "class D"); delete ptr; }   該程序的輸出結(jié)果為:   class A   class B   class C   class D   在派生類B和C中使用了虛基類,使得建立的D類對象只有一個(gè)虛基類子對象。   在派生類B,C,D的構(gòu)造函數(shù)的成員初始化列表中都包含了對虛基類A的構(gòu)造函數(shù)。   在建立類D對象時(shí),只有類D的構(gòu)造函數(shù)的成員初始化列表中列出的虛基類構(gòu)造函數(shù)被調(diào)用,并且僅調(diào)用一次,而類D基類的構(gòu)造函數(shù)的成員初始化列表中列出的虛基類構(gòu)造函數(shù)不被執(zhí)行。這一點(diǎn)將從該程序的輸出結(jié)果可以看出。    
          posted on 2006-01-17 19:41 EricWong 閱讀(230) 評論(0)  編輯  收藏 所屬分類: C&C++
          主站蜘蛛池模板: 迁安市| 拉萨市| 金川县| 张家港市| 梁河县| 定结县| 侯马市| 崇左市| 宜兰市| 板桥市| 扶余县| 铁岭县| 新沂市| 夏津县| 无锡市| 盱眙县| 福安市| 仙居县| 东城区| 泽库县| 芜湖县| 马公市| 蒲江县| 凌海市| 六枝特区| 南岸区| 台南市| 济南市| 吴桥县| 苍山县| 江油市| 墨玉县| 化隆| 昆山市| 佛教| 久治县| 温宿县| 玛曲县| 罗甸县| 马山县| 石首市|