本文聯(lián)合編輯:小辣辣。向她致以最崇高的敬(愛)意 ?
第一章 C++的初步認識
在程序進行編譯時,先對所有的預處理命令進行處理,將頭文件的具體內(nèi)容代替 #include 指令,然后再對該程序單元進行整體編譯。
對函數(shù)做聲明,它的作用是通知 C++ 編譯系統(tǒng)。
第二章 數(shù)據(jù)的存儲、表示形式和基本運算
C++ 沒有規(guī)定每一種數(shù)據(jù)所占的字節(jié)數(shù),只規(guī)定 int 型數(shù)據(jù)所占的字節(jié)數(shù)不大于 long 型,不小于 short 型。
符號常量
#define PRICE 30
符號常量雖有名字,但它不是變量。在進行編譯預處理時,所有的 PRICE 都被置換為字符 30,在正式進行編譯時已經(jīng)沒有 PRICE 這個標識符了。但 C++ 程序員一般更喜歡用 const 定義常變量。
運算符
/除法運算符
結果是整數(shù),如果有一個是負數(shù),則舍入方向是不固定的。多數(shù)編譯系統(tǒng)采取向零取整的方法%模運算符
兩側均應為整型數(shù)據(jù),如 7%4 的值為 3
+,–,*,/ 運算中的兩個數(shù)中有一個數(shù)為 float 型數(shù)據(jù),則運算結果是 double 型,因為 C++ 在運算時對所有 float 型數(shù)據(jù)都按 double 型數(shù)據(jù)處理。
進行運算時,不同類型的數(shù)據(jù)都要先轉換成同一類型,然后進行運算。
自增(減)運算符
也用于指針變量,使指針指向下一個地址。
賦值運算符和賦值表達式
賦值過程中的類型轉換
將浮點型數(shù)據(jù)賦值給整型變量時,舍棄其小數(shù)部分。
不同類型的整型數(shù)據(jù)間的賦值歸根到底就是一條:按存儲單元中的存儲形式直接傳送。
程序初步設計
關系運算和邏輯運算
運算符優(yōu)先級:
算數(shù)運算符 > 關系運算符 >賦值運算符
在 C 和 C++ 中都用數(shù)據(jù) 1 代表真,0 代表假。編譯系統(tǒng)在處理邏輯型數(shù)據(jù)時,將 false 處理為 0,將 true 處理為 0。邏輯變量在內(nèi)存中占 1 個字節(jié),用來存放 0 或 1。
用 for 語句構成循環(huán)
for(表達式1;表達式2;表達式3)語句
表達式 1 可以省略,但其后得分號不可省略。
如果表達式 2 省略,即不判斷循環(huán)條件,循環(huán)無休止地進行下去。
第四章 利用函數(shù)實現(xiàn)指定的功能
函數(shù)參數(shù)和函數(shù)的值
在調(diào)用函數(shù)時,編譯系統(tǒng)臨時給形參分配存儲單元。調(diào)用結束后,形參單元被釋放,實參單元仍保留并維持原值。因此,在執(zhí)行一個被調(diào)用函數(shù)時,形參的值如果發(fā)生改變,并不會改變主調(diào)用函數(shù)中實參的值。
對被調(diào)用函數(shù)的聲明和函數(shù)原型
在函數(shù)聲明中可以不寫形參名,只寫形參類型
內(nèi)置函數(shù)(內(nèi)聯(lián)函數(shù))
轉去被調(diào)用函數(shù)前,要記下當時執(zhí)行的指令的地址,還要記下當時有關的信息,以便在函數(shù)調(diào)用后繼續(xù)執(zhí)行,如果有的函數(shù)需要頻繁使用,則所用時間會很長,從而降低執(zhí)行效率??梢允褂脙?nèi)置函數(shù)將所調(diào)用的函數(shù)的代碼直接嵌入到主調(diào)函數(shù)中,而不是將流程轉出去。使用關鍵字 inline。
函數(shù)模板
template < typename T > //類似 Java 的泛型
有默認參數(shù)的函數(shù)
注意點:如果函數(shù)的定義在函數(shù)調(diào)用之前,則應在函數(shù)定義中給出默認值。如果函數(shù)的定義在函數(shù)調(diào)用之后,則在函數(shù)調(diào)用之前需要有函數(shù)聲明,此時必須許多函數(shù)聲明中給出默認值,函數(shù)定義時可以不給出默認值。
變量的存儲類型
-
auto(默認)
自動變量,動態(tài)局部變量,函數(shù)調(diào)用結束后釋放
-
static
靜態(tài)局部變量在靜態(tài)存儲區(qū)內(nèi)分配存儲單元,在程序整個運行期間都不釋放。
加上 static 聲明,則該變量只能用于本文件 -
register
寄存器變量,如果有一些變量使用頻繁,則為存取變量的值要花不少時間,為提高效率,C++ 允許將局部變量的值放在 cpu 的寄存器中
-
extern
全局變量(外部變量),作用域從變量的定義開始,到本程序文件的末尾
注意 extern 是用作變量聲明,而不是變量定義。它只是對一個已定義的外部變量做聲明,以擴展其作用域。
內(nèi)部函數(shù)和外部函數(shù)
外部函數(shù)
如果在函數(shù)的首部的最左端冠以關鍵字 extern,則表示此函數(shù)是外部函數(shù),可供其他文件調(diào)用。
第五章 利用數(shù)組處理批量數(shù)據(jù)
一維數(shù)組的初始化
- 可以只給一部分元素賦值(后面的元素默認為 0)
- 對全部數(shù)組元素賦初值時,可以不指定數(shù)組長度
二維數(shù)組的初始化
- 可以對部分元素賦值
用數(shù)組作函數(shù)參數(shù)
數(shù)組名作實參和形參,傳遞的是數(shù)組的起始地址。
數(shù)組名代表數(shù)組首元素的地址。
用數(shù)組名作實參,如果改變了形參數(shù)組元素的值將同時改變實參數(shù)組元素的值。
字符數(shù)組
定義和初始化字符數(shù)組
如果提供的初值個數(shù)大于數(shù)組長度,則按語法錯誤處理。如果小于數(shù)組長度,則只將字符賦值給數(shù)組中前面的元素,其余的元素自動定義為空字符 \0
字符數(shù)組的賦值和引用
只能對字符數(shù)組的元素賦值,而不能用賦值語句對整個數(shù)組賦值。
結束標志
遇到字符 \0 就表示字符串到此結束。對于一個字符串常量,系統(tǒng)會自動在所在字符的后面加一個 \0 作為結束符,然后再把它存在字符數(shù)組中。
可以用字符串常量來初始化字符數(shù)組。
字符數(shù)組的輸入和輸出
- 輸出得字符不包含結束符
\0
字符串處理函數(shù)
比較函數(shù) strcmp
strcmp(str1,str2)
對兩個字符串自左至右逐個字符相比(按 ASCII 值大小比較)
兩個字符串比較,不能用以下形式:
if(str1 > str2) {
...
}
上面寫法表示將兩個數(shù)組的地址進行比較。
字符串變量的定義和引用
和其他類型變量一樣,字符串變量必須先定義后使用。
第六章 善于使用指針和引用
i_pointer 是一個指針變量,* i_pointer 表示 i_pointer 所指向的變量。
* 不是指針變量名的一部分,在變量名前加一個 * 表示該變量是指針變量。
不能用一個整數(shù)給指針變量賦值。
int * pointer = 2000;
編譯系統(tǒng)并不把 2000 認為是地址,而認為是整數(shù)??梢詫⒁粋€已定義的變量的地址作為指針變量的初值。
一個指針只能指向同一個類型的變量。
引用指針變量
& : 取地址運算符
&a 為變量 a 的地址, *p 為指針變量 p 所指向的存儲單元
int * pointer = & a //定義指針變量
pointer:指針變量
* pointer 等效于 a(對指針變量 pointer 做 * 運算,指向 pointer 指向的存儲單元,即 a)
& 和 * 兩個運算符的優(yōu)先級別相同,但按自右而左方向結合。例如已知 pointer = &a,& * pointer的含義是:先進行 *pointer 的運算,它就是變量 a,再執(zhí)行 & 運算,因此 & * pointer 與 & a 相同,即變量 a 的地址。
用指針作函數(shù)參數(shù)
int main() {
void swap(int * p1, int * p2)
int * pointer_1, * pointer_2, a, b;
cin >> a >> b;
pointer_1 = &a;
pointer_2 = &b;
swap(pointer_1, pointer_2)
}
void swap(int * p1, int * p2) { //定義指針變量 p1, p2,即 swap 的形參須是指針
int temp;
temp = * p1; // * p1:對指針變量做 * 運算,指向值
...
}
不能試圖通過改變形參指針變量的值而使實參指針變量的值改變。實參和形參之間的數(shù)據(jù)傳遞是單向的“值傳遞”的方式,指針變量作函數(shù)參數(shù)也要遵循這一規(guī)則,調(diào)用函數(shù)時不會改變實參指針變量的值,但可以改變實參指針變量指向變量的值。
int main(int argc, char* argv[])
{
void myswap(int * p1, int * p2);
int * point1, * point2, a, b;
a = 10, b = 20;
point1 = &a;
point2 = &b;
cout<<"a"<<a<<"b"<<b<<endl;
if(a < b) myswap(point1, point2);
cout<<"a"<<a<<"b"<<b<<endl;
if(a < b) realswap(point1, point2);
cout<<"a"<<a<<"b"<<b<<endl;
return 0;
}
//指針變量也是值傳遞,形參修改并不影響實參 point1 指針
void myswap(int * p1, int * p2) {
int * temp;
temp = p1;
p1 = p2;
p2 = temp;
}
void realswap(int * p1, int * p2) {
int temp;
temp = * p1;
* p1 = * p2; //* p1,直接修改 p1 指向的值(a)的值
* p2 = temp;
}
//輸出
a10b20
a10b20
a20b10
//分析
point1 = & a
* point1 即 point1 所指向的變量
數(shù)組和指針
指向數(shù)組元素的指針
int a[10]; //定義一個數(shù)組,有 10 個元素
int *p;
p = &a[0] //將元素 a[0] 的地址賦給指針變量 p,使 p 指向 a[0]
在 C++ 中,數(shù)組名代表數(shù)組中的第 1 個元素(即序號為 0 的元素)的地址,因此以下語句等價:
p = &a[0];
p = a;
數(shù)組名 a 不代表整個數(shù)組,p=a 的作用是把 a 數(shù)組的首元素的地址賦給指針變量 p。
如果指針變量 p 已指向數(shù)組中的一個元素,則 p + 1 指向同一個數(shù)組中的下一個元素。
如果 p 的初值為 &a[0],那么 p + i 和 a + i 就是 a[i] 的地址。
*(p+5)
*(a+5)
a[5]
這三種表示方法等價。
可以看出,數(shù)組名后面的括號 [],實際上是變址運算符,指向數(shù)組元素的指針變量也可以帶下標,如 p[i] 與 *(p+i) 等價。
效率比較:
-
下標法:a[i]
int a[10]; int i; for(i = 0; i < 10; i++) { cout<<a[i]<<endl; } -
指針法:*(a + i)
int a[10]; int i; for(i = 0; i < 10; i++) { cout<<*(a + i)<<endl; } -
用指針變量指向數(shù)組元素
int a[10]; int i; for(p = a; p < (a + 10); p++) { cout<<*p<<endl; }
1 和 2 的執(zhí)行效率相同,C++ 編譯系統(tǒng)是將 a[i] 轉換為 *(a + 1) 處理的,對每個 a[i] 都分別計算地址 a + i x d,然后訪問該元素。3 比 1 和 2 快,用指針變量直接指向元素,不必每次都重新計算地址。
在使用指針變量指向數(shù)組元素是,應切實保證指向數(shù)組中有效的元素。
C++ 編譯系統(tǒng)將形參數(shù)組名一律作為指針變量來處理。
函數(shù)與指針
只須將函數(shù)名 max 賦給 p,不能寫成 p = max(a,b)
int * a(int x, int y);
a 是函數(shù)名,調(diào)用它以后m能得到一個指向整型數(shù)據(jù)的指針(地址)。
指針數(shù)組和指向指針的指針
指針數(shù)組
int * p[4]
由于 [] 比 * 優(yōu)先級高,因此 p 先與 [4] 結合,形成 p[4] 形式,這顯然是數(shù)組形式。不要寫成 int 這是指向一維數(shù)組的指針變量。
* (name + i ++)
表示先求 * 的值,即 name 它是一個地址。將它賦給 p,然后 i 加 1,最后輸出以 p 地址開始的字符串。
指向指針的指針
由于 name[i] 的值是地址(即指針),因此 name + i 就是指向指針型數(shù)據(jù)的指針。
// 指針變量(比如變量 a 的地址)
char * p
// 存儲 a 的地址的指針的地址
char *(* p)
* p = 'C++'
** p 指向 ‘C++’ 的第一個字符元素的內(nèi)容
const 指針
用指向常量的指針變量只是限制了通過指針變量改變它指向的對象的值。
-
const 類型名 * p (指向常量的指針變量)
p 的指向可變,但 p 指向的對象的值不可變。
int a = 12; int b = 15; const int * p = & a; p = &b; //合法,p 的指向可變 * p = 20; //非法,p 指向的對象的值不可變 a = 15; //合法,a 不是 const 常量如果想絕對保證 a 的值始終不變,應當把 a 定義為常變量。
-
int * const p (常指針變量)
指針變量的指向不可變,但指向變量的值可變
int a = 12; int b = 15; int * const p = & a; p = &b; //非法,p 的指向不可變 * p = 20; //合法,p 指向變量的值可變 -
const int a
a 的值不可變
const int a = 12; a = 20; //非法
void 指針類型
該空間尚未使用,其中沒有數(shù)據(jù),談不上指向什么類型的數(shù)據(jù),故返回一個 void * 類型的指針,表示它不指向確定的具有類型的數(shù)據(jù)。
指針運算
指針變量可以有空值,即該指針變量不指向任何變量,它可以這樣表示:
// iostream 頭文件中已定義了符號常量 NULL 代表整數(shù) 0
p = NULL;
如果兩個指針不指向同一個數(shù)組,則比較是無意義的。如果一定要對不同類型的指針變量賦值,可以用到強制類型轉換。
引用
在數(shù)據(jù)類型名后面出現(xiàn)的 & 是引用聲明符號,在其他場合出現(xiàn)的都是地址符:
char &d = c; // 引用的聲明符
int * p = & a; //地址符
在聲明一個引用后,不能再使之作為另一變量的引用。
int a1, a2;
int &b = a1;
int &b = a2; //非法
引用其實就是一個指針常量,它的指向不可改變。
引用作為函數(shù)參數(shù)
傳遞變量的地址:形參是指針變量,實參是一個變量的地址。調(diào)用函數(shù)時,形參(指針變量)得到實參變量的地址,因此指向?qū)崊⒆兞繂卧?/p>
實參不是地址而是整型變量名,由于形參是引用,系統(tǒng)會自動將實參的地址傳遞給形參,注意:此時傳送的是實參變量的地址而不是實參變量的值。實參是地址,傳遞的是地址,故仍然是值傳遞。方式(3)中實參是變量名,而傳遞的是變量的地址,這才是傳址方式。
第七章 用戶自定義數(shù)據(jù)類型
引用結構體變量
. 是成員運算符,它在所有運算符中優(yōu)先級最高,因此可以把 student.num 作為一個整體來看待。
指向結構體變量的指針
C 和 C++ 提供了指向結構體變量的運算符 ->,形象的表示“指向”關系。例如,p->num 表示指針 p 當前指向的結構體變量中的成員 num。
p->n++ 得到p指向的結構體變量中的成 員 n的值,用完該值后使它加 1。
++p->n 得到 p 指向的結構體變量中的成員 n 的值,并使之加 1,然后再使用它。
用結構體變量和指向結構體變量的指針構成鏈表
用結構體變量和指向結構體變量的指針構成鏈表,最有一個元素不再指向其他元素,它成為“表尾”,它的地址部分放到一個“NULL”(表示“空地址”),鏈表到此結束。
所有節(jié)點(結構體變量)都是在程序中定義的,不是臨時開辟的,也不能用完后釋放,這種鏈表成為靜態(tài)鏈表。
動態(tài)鏈表則是指個結點是可以隨時插入和刪除的,這些節(jié)點并沒有變量名,只能先找到一個結點上,才能根據(jù)它提供的下一個結點的地址找到下一個結點。
結構體類型數(shù)據(jù)最為函數(shù)參數(shù)
調(diào)用函數(shù)時形參要單獨開辟內(nèi)存單元,如果結構體變量占的存儲空間很大,則在虛實結合時控件和時間的開銷都比較大,效率是不高的。
用 new 和 delete 運算符進行動態(tài)分配和撤銷存儲空間
new 運算符使用的一般格式:
new 類型 [初值]
注意:用 new 分配數(shù)組空間時不能指定初始值
delete 運算符使用的一般格式為:
delete 指針變量
或
delete [] 指針變量
在指針變量前面加一對方括號,表示是對數(shù)組空間的操作。
枚舉類型
枚舉元素按常量處理,故稱枚舉常量,它們不是常量,不能對它們賦值,即枚舉元素的值是固定的。
第八章 類和對象的特性
聲明類類型
如果在類的定義中既不指定 private,也不指定 public,則系統(tǒng)就默認為是私有的。
除了 private 和 public之外,還有一種成員訪問限定符 protect(受保護的),用protect聲明的成員不能被類外訪問(這點與私有成員類似),但可以被派生類的成員函數(shù)訪問。
成員函數(shù)的性質(zhì)
:: 是作用域限定符(field qualifier)或稱作用域運算符,用它聲明函數(shù)是屬于哪個子類的。
函數(shù)名前既無類名又無作用域運算符,表示函數(shù)不屬于任何類,這個函數(shù)不是成員函數(shù),而是全局函數(shù)。
內(nèi)置成員函數(shù)
在類體中定義的成員函數(shù)的規(guī)模一般都很小,而系統(tǒng)調(diào)用函數(shù)的過程所花費的時間開銷相對是比較大的,為減少時間的開銷,如果在類體中定義的成員函數(shù)中不包括循環(huán)等控制結構,C++ 系統(tǒng)就自動把它們作為內(nèi)置(inline)函數(shù)來處理。
成員函數(shù)的存儲方式
每個對象所占用的存儲空間只是該對象的數(shù)據(jù)成員所占的存儲空間,而不包括函數(shù)代碼所占用的存儲空間。
第九章 怎樣使用類和對象
對象的初始化
不能在類生命中對數(shù)據(jù)成員初始化,因為類并不是一個實體,而是一種抽象類型,并不占存儲空間,顯然誤觸容納數(shù)據(jù)。
用構造函數(shù)實現(xiàn)數(shù)據(jù)成員的初始化
在類外定義構造成員函數(shù),要加上類名和域限定符。
在建立對象時系統(tǒng)為該對象分配存儲單元,此時執(zhí)行構造函數(shù)。
構造函數(shù)的重載
無參構造函數(shù)應注意正確書寫定義對象的語句
請記?。簶嬙旌瘮?shù)是不能被用戶顯式調(diào)用的
使用默認參數(shù)的構造函數(shù)
由于不需要實參也可以調(diào)用構造函數(shù),因此全部參數(shù)都指定了一個默認值的構造函數(shù)也屬于默認的構造函數(shù)。
編譯系統(tǒng)無法識別應該調(diào)用那個構造函數(shù),出現(xiàn)歧義性
在一個類中定義了全部是默認參數(shù)的構造函數(shù)后,不能再定義重載構造函數(shù)
析構函數(shù)
如果用戶沒有定義析構函數(shù),C++ 編譯系統(tǒng)會自動生成一個析構函數(shù),但它只是徒有析構函數(shù)的名稱和形式,實際上什么操作都不進行。
調(diào)用構造函數(shù)和析構函數(shù)的順序
先構造的后析構,后構造的先析構。相當于一個棧,先進后出。
指向?qū)ο蟪蓡T的指針
Time * pt; //定義 pt 是指向 Time 類對象的指針變量
Time tl; //定義 tl 為 Time 類對象
pt = & tl; //將 tl 的起始地址賦給 pt
* pt; //pt 所指向的對象,即 tl;
//以下表示是等價的
(* pt).hour;
pt -> hour;
指針變量的類型必須與賦值號右側函數(shù)的類型相匹配,要求在以下3方面都要匹配:1.函數(shù)參數(shù)的類型和參數(shù)個數(shù);2、函數(shù)返回值類型;3.所屬的類
定義指向公用成員函數(shù)的指針變量的一般形式為:
數(shù)據(jù)類型名 (類名 :: * 指針變量名)(參數(shù)列表);
//形如:
void(Time:: *p2)()
使指針變量指向一個公用成員函數(shù)的一般形式為:
指針變量名 = & 類名 :: 成員函數(shù)名
//形如
p2 = &Time::get_time;
常對象
常對象必須有初值,在常對象的生命周期中,對象中的所有數(shù)據(jù)成員的值都不能被修改
定義常對象的一般形式為:
類名 const 對象名[(實參表)];
也可以把const寫在最左邊:
const 類名 對象名[(實參表)];
與上面的格式是等價的。
在定義常對象時,必須同時對之初始化,之后不能再改變。
常對象成員
-
常成員函數(shù)
//注意const的位置在函數(shù)名和括號之后 viod get_time() const;
指向?qū)ο蟮某V羔?/h3>
指向?qū)ο蟮某V羔樧兞康闹挡荒芨淖?,即始終指向同一個對象,但可以改變其所指向的對象的值。
往往常指針作為函數(shù)的形參,目的是不允許在函數(shù)執(zhí)行過程中改變指針變量的值,使其始終指向原來的對象。
指向常對象的指針變量
一個對象已經(jīng)被聲明為常變量,只能用指向常變量的指針變量指向它。
指向常變量的指針變量除了可以指向常變量,還可以指向未被聲明為const的變量。此時不能通過此指針變量來改變該變量的值。
指向常對象的指針最常用于函數(shù)的形參,目的是在白虎形參指針所指向的對象,使它在函數(shù)執(zhí)行過程中不被修改。
以下是非法的:
- 形參:指向非 const 型變量的指針;實參 const 變量的地址;
因為參數(shù)傳遞本質(zhì)是值傳遞/地址傳遞。過程:形參指向?qū)崊?。因為?const 型變量指針只能指向非 const 型變量。所以以上是非法的。
在函數(shù)調(diào)用時將建立一個新的對象,它是實參對象的拷貝
對象的動態(tài)建立和釋放
用new運算符動態(tài)地分配內(nèi)存后,將返回一個指向新對象的指針。
對象的賦值
對象的賦值只對其中數(shù)據(jù)成員賦值,而不對成員函數(shù)賦值。
不同對象的成員函數(shù)時同一個函數(shù)代碼段,不需要,也無法對它賦值。
靜態(tài)成員
靜態(tài)數(shù)據(jù)成員可初始化,但只能在類體外進行初始化。
公用靜態(tài)數(shù)據(jù)成員與全局變量不同,靜態(tài)數(shù)據(jù)成員的作用域只限于定義該類的作用域內(nèi)(如果是在一個函數(shù)中定義類,那么其中靜態(tài)數(shù)據(jù)成員的作用域就是在此函數(shù)內(nèi))
靜態(tài)數(shù)據(jù)成員函數(shù)
非靜態(tài)成員函數(shù)有 this 指針,而靜態(tài)成員函數(shù)沒有this指針。由此決定了靜態(tài)成員函數(shù)不能訪問本類中的非靜態(tài)成員。
在 C++ 程序中最好養(yǎng)成這樣的習慣:只用靜態(tài)成員函數(shù)引用靜態(tài)數(shù)據(jù)成員,而不引用非靜態(tài)數(shù)據(jù)成員。
友元函數(shù)
在類外定義的且未用類最限定的函數(shù),是非成員函數(shù),不屬于任何類。
友元類
- 友元的關系是單向的而不是雙向的。
- 友元的關系不能傳遞。
類模板
一般形式為:
類模板名 <實際類型名> 對象名(參數(shù)表);
運算符重載
重載運算符的規(guī)則
重載的運算符必須和用戶定義的自定義類型的對象一起使用,其參數(shù)至少應有一個是類對象(或類對象的引用),參數(shù)不能全是 C++ 的標準類型
運算符重載函數(shù)作為類成員函數(shù)和友元函數(shù)
將雙目運算符重載為友元函數(shù)時,由于友元函數(shù)不是該類的成員函數(shù),因此在函數(shù)的形參表中必須有兩個參數(shù),不能省略。
用轉換構造函數(shù)進行不同類型數(shù)據(jù)的轉換
通常把有一個參數(shù)的構造函數(shù)作類型轉換,所以,稱為轉換構造函數(shù)。
類型轉換函數(shù)
類型轉換函數(shù)的作用是將一個類的對象轉換成另一個類型的數(shù)據(jù)。形式如:
operator 類型名()
{ ... }
在函數(shù)名前不能指定函數(shù)類型,函數(shù)沒有參數(shù)。類型轉換函數(shù)只能作為成員函數(shù),因為轉換的主體是本類的對象,不能作為友元函數(shù)或普通函數(shù)。
如果運算符重載函數(shù)為成員函數(shù),它的第一個參數(shù)必須是本類的對象。當?shù)谝粋€操作數(shù)不是類對象時,不能將運算符函數(shù)重載為成員函數(shù),如果將運算符“+” 重載為類的成員函數(shù),交換律不適用。
類型轉換函數(shù)與運算符重載不共存。因為可能出現(xiàn)二義性。
第十一章 繼承與派生
派生類的聲明方式
基類名前有 public 的稱為公用繼承。如果不寫此項,默認為 privite(私有的)
派生類成員的訪問屬性
基類的成員函數(shù)只能訪問基類的成員,而不能派生類的成員。
-
公用繼承
基類的公用成員和保護成員在派生類中保持原有訪問屬性,私有成員仍為基類私有
-
私有繼承
基類的公有成員和保護成員在派生類中成了私有成員,私有成員仍為基類私有
-
受保護繼承
基類的公有成員和保護成員在派生類中成了保護成員,私有成員仍為基類私有
派生類的構造函數(shù)和析構函數(shù)
基類的構造函數(shù)式不能繼承的,對繼承過來的基類成員初始化的工作也要由派生類的構造函數(shù)承擔。解決這個問題的思路是:在執(zhí)行派生類的構造函數(shù)時,調(diào)用基類的構造函數(shù)。
簡單的派生類構造函數(shù)
在類中對派生類構造函數(shù)作聲明時,不包括上面給出的一般形式中的基類的構造函數(shù)名(參數(shù)表)部分
有子對象的派生類的構造函數(shù)
應當在建立對象時對它的數(shù)據(jù)成員初始化。
派生類構造函數(shù)的任務應該包括:
- 對基類數(shù)據(jù)成員初始化
- 對子對象數(shù)據(jù)成員初始化
- 對派生類數(shù)據(jù)成員初始化
定義派生類構造函數(shù)的一般形式為:
派生類構造函數(shù)名(總參數(shù)表):基類構造函數(shù)名(參數(shù)表),子對象名(參數(shù)表)
{ 派生類中新增數(shù)據(jù)成員初始化語句 }
派生類構造函數(shù)的特殊形式
如果在基類中沒有定義構造函數(shù),或定義了沒有參數(shù)的構造函數(shù),那么,在定義派生類構造函數(shù)時可以不寫基類構造函數(shù)。因此此時派生類構造函數(shù)沒有向基類構造函數(shù)傳遞參數(shù)的任務。在調(diào)用派生類構造函數(shù)時,系統(tǒng)會自動首先調(diào)用基類的默認構造函數(shù)。
派生類的析構函數(shù)
在執(zhí)行派生類的析構函數(shù)時,系統(tǒng)會自動調(diào)用基類的析構函數(shù)和子對象的析構函數(shù)。調(diào)用的順序與構造函數(shù)相反:先執(zhí)行派生類自己的析構函數(shù),然后調(diào)用子對象的析構函數(shù),最后調(diào)用基類的析構函數(shù)。
多重繼承
形式如:
派生類構造函數(shù)名(總參數(shù)表):基類 1 構造函數(shù)(參數(shù)表),基類 2 構造函數(shù)(參數(shù)表), 基類 3 構造函數(shù)(參數(shù)表)
{ 派生類中新增數(shù)據(jù)成員初始化語句 }
聲明基類的順序決定了基類構造函數(shù)的調(diào)用順序
多重繼承引發(fā)的二義性問題
基類的同名成員在派生類中被屏蔽。成為不可見的。因此如果在定義派生類對象的模塊中通過對象名訪問同名的成員,則訪問的是派生類的成員。請注意:不同的成員函數(shù),只有在函數(shù)名和參數(shù)個數(shù)相同、類型相匹配的情況下才發(fā)生同名覆蓋。
虛基類
虛基類使得在繼間接共同基類時只保留一份成員。
需要注意,為了保證虛基類在派生類中只繼承一次,應當在該基類的所有直接派生類中聲明為虛基類,否則仍然會出現(xiàn)對基類的多次繼承。
由于虛基類在派生類中只有一份數(shù)據(jù)成員,所以這份數(shù)據(jù)成員的初始化必須由派生類直接給出。
C++ 編譯系統(tǒng)只執(zhí)行最后的派生類對虛基類的構造函數(shù)的調(diào)用,而忽略虛基類的其他派生類對虛基類的構造函數(shù)的調(diào)用,這就保證了虛基類的數(shù)據(jù)成員不會被多次初始化。
基類與派生類的轉換
A al; //定義基類 A 對象 al
B bl; //B 是 A 的派生類
A& r = al; //定義基類 A 對象的引用 r,并引用 al 對其初始化
這時,r 是 al 的引用(別名),r 和 al 共享同一段存儲單元。
A& r = bl;
此時 r 并不是 bl 的別名,也不是與 bl 共享同一段存儲單元,它只是 bl 中基類部分的別名,r 與 bl 中基類部分共享同一段存儲單元,r 與 bl 具有相同的其實地址。
即基類的引用類型,指向了派生類的變量。指向的也是派生類中從基類繼承的部分。
繼承與組合
在一個類中以另一個類的對象作為數(shù)據(jù)成員的,稱為類的組合。
第十二章 多態(tài)性與虛函數(shù)
派生類對象可以替代基類對象向基類對象的引用初始化或賦值。調(diào)用的不是在 Circle 中聲明的運算符重載函數(shù),而是在 Point 中聲明的運算符重載函數(shù),輸出的是“點”的信息,而不是“圓”的信息。
這兩個 area 函數(shù)不是重載函數(shù),它們不僅函數(shù)名相同,而且函數(shù)類型和參數(shù)個數(shù)都相同,兩個同名函數(shù)不在同一個類中,而是分別在基類和派生類中,屬于同名覆蓋。
虛函數(shù)的作用
編譯系統(tǒng)按照同名覆蓋的原則決定調(diào)用的對象。
C++ 的虛函數(shù)就是用來解決動態(tài)多態(tài)的問題的。所謂虛函數(shù),就是在基類聲明函數(shù)是虛擬的,并不是實際存在的函數(shù),然后在派生類中才正式定義此函數(shù)。
本來,基類指針是用來指向基類對象的,如果用它指向派生類對象,則自動進行指針類型轉換,將派生類的對象的指針先轉換為基類的指針,這樣,基類指針指向的是派生類中的基類部分。
有時在基類中定義的非虛函數(shù)會在派生類中被重新定義,如果用基類指針調(diào)用該成員函數(shù),則系統(tǒng)會調(diào)用對象中基類部分的成員函數(shù);如果用派生類指針調(diào)用該函數(shù),則系統(tǒng)會調(diào)用派生類對象中的成員函數(shù),這并不是多態(tài)性行為(使用的是不同類型的指針)
靜態(tài)關聯(lián)和動態(tài)關聯(lián)
確定調(diào)用的具體對象的過程稱為關聯(lián)。
函數(shù)重載和通過對象名調(diào)用的虛函數(shù),在編譯時即可確定其調(diào)用的虛函數(shù)屬于哪一個類,其過程稱為靜態(tài)關聯(lián)。
由于是在運行階段把虛函數(shù)和類對象“綁定”在一起的,因此稱為動態(tài)關聯(lián)。
在什么情況下應當聲明虛函數(shù)
一個成員函數(shù)被聲明為虛函數(shù)后,在同一類族中的類就不能定義一個非 virtual 的但與該虛函數(shù)具有相同的參數(shù)。
虛析構函數(shù)
先調(diào)用了派生類的析構函數(shù),再調(diào)用了基類的析構函數(shù)。當基類的析構函數(shù)為虛函數(shù),無論指針指的是同一類族中的哪一個類對象。
如果將基類的析構函數(shù)聲明為虛函數(shù)時,由該函數(shù)所派生的所有派生類的析構函數(shù)有都自動成為虛函數(shù),即使派生類的析構函數(shù)與基類的析構函數(shù)名字不相同。
純虛函數(shù)與抽象類
virtual 函數(shù)類型 函數(shù)名(參數(shù)類別) = 0;
純虛函數(shù)只有函數(shù)的名字而不具備函數(shù)的功能,不能被調(diào)用。它只是通知編譯系統(tǒng):在這里聲明一個虛函數(shù),待派生類中定義。
抽象類
凡是包含純虛函數(shù)的類的都是抽象類。因為純虛函數(shù)是不能被調(diào)用的,包含純虛函數(shù)的類是無法建立對象的。抽象類的作用是作為一個類族的共同基類。
抽象基類不能也不必要定義對象。
區(qū)別靜態(tài)關聯(lián)和動態(tài)關聯(lián)
在編譯階段就能確定調(diào)用的是哪一個類的虛函數(shù),所以屬于靜態(tài)關聯(lián)。如果是通過基類指針調(diào)用虛函數(shù),在編譯階段無法從語句本身確定調(diào)用哪一個類的虛函數(shù),只有在運行時,指針指向某一類對象后,才能確定調(diào)用的是哪一個類的虛函數(shù),故為動態(tài)關聯(lián)。
第十三章 輸入輸出流
istream 類的其他成員函數(shù)
如果到達文件末尾(遇到文件結束符),eof 函數(shù)值為非零值(表示真),否則為 0(假)。