C++ 拷貝構(gòu)造函數(shù)
- 在創(chuàng)建對象時(shí),是使用同一類中之前創(chuàng)建的對象來初始化新創(chuàng)建的對象
- 如果在類中沒有定義拷貝構(gòu)造函數(shù),編譯器會自行定義一個(gè)
- 如果類帶有指針變量,并有動態(tài)內(nèi)存分配,則它必須有一個(gè)拷貝構(gòu)造函數(shù)
class Line
{
public:
int getLength( void );
Line( int len ); // 簡單的構(gòu)造函數(shù)
Line( const Line &obj); // 拷貝構(gòu)造函數(shù)
~Line(); // 析構(gòu)函數(shù)
private:
int *ptr;
};
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(int len)
{
cout << "調(diào)用構(gòu)造函數(shù)" << endl;
// 為指針分配內(nèi)存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷貝值
}
Line::~Line(void)
{
cout << "釋放內(nèi)存" << endl;
delete ptr;
}
C++ 友元函數(shù)
- 類的友元函數(shù)是定義在類外部,但有權(quán)訪問類的所有私有(private)成員和保護(hù)(protected)成員。
- 友元函數(shù)并不是成員函數(shù)
- 友元可以是一個(gè)函數(shù),該函數(shù)被稱為友元函數(shù);友元也可以是一個(gè)類,該類被稱為友元類,在這種情況下,整個(gè)類及其所有成員都是友元
- 如果要聲明函數(shù)為一個(gè)類的友元,需要在類定義中該函數(shù)原型前使用關(guān)鍵字 friend
C++ 內(nèi)聯(lián)函數(shù)
- 如果一個(gè)函數(shù)是內(nèi)聯(lián)的,那么在編譯時(shí),編譯器會把該函數(shù)的代碼副本放置在每個(gè)調(diào)用該函數(shù)的地方
- 對內(nèi)聯(lián)函數(shù)進(jìn)行任何修改,都需要重新編譯函數(shù)的所有客戶端,因?yàn)榫幾g器需要重新更換一次所有的代碼,否則將會繼續(xù)使用舊的函數(shù)
- 如果想把一個(gè)函數(shù)定義為內(nèi)聯(lián)函數(shù),則需要在函數(shù)名前面放置關(guān)鍵字 inline,在調(diào)用函數(shù)之前需要對函數(shù)進(jìn)行定義
- 如果已定義的函數(shù)多于一行,編譯器會忽略 inline 限定符
- 在類定義中的定義的函數(shù)都是內(nèi)聯(lián)函數(shù),即使沒有使用 inline 說明符
C++ this 指針
- C++ 中,每一個(gè)對象都能通過 this 指針來訪問自己的地址
- this 指針是所有成員函數(shù)的隱含參數(shù)。因此,在成員函數(shù)內(nèi)部,它可以用來指向調(diào)用對象
- 友元函數(shù)沒有 this 指針,因?yàn)橛言皇穷惖某蓡T。只有成員函數(shù)才有 this 指針
C++ 指向類的指針
- 訪問指向類的指針的成員,需要使用成員訪問運(yùn)算符 ->,就像訪問指向結(jié)構(gòu)的指針一樣
C++ 類的靜態(tài)成員
- 可以使用 static 關(guān)鍵字來把類成員定義為靜態(tài)的
- 聲明類的成員為靜態(tài)時(shí),這意味著無論創(chuàng)建多少個(gè)類的對象,靜態(tài)成員都只有一個(gè)副本
- 靜態(tài)成員在類的所有對象中是共享的
- 創(chuàng)建第一個(gè)對象時(shí),所有的靜態(tài)數(shù)據(jù)都會被初始化為零
- 不能把靜態(tài)成員的初始化放置在類的定義中,但是可以在類的外部通過使用范圍解析運(yùn)算符 :: 來重新聲明靜態(tài)變量從而對它進(jìn)行初始化
int Box::objectCount = 0;
靜態(tài)成員函數(shù)
- 如果把函數(shù)成員聲明為靜態(tài)的,就可以把函數(shù)與類的任何特定對象獨(dú)立開來。靜態(tài)成員函數(shù)即使在類對象不存在的情況下也能被調(diào)用,靜態(tài)函數(shù)只要使用類名加范圍解析運(yùn)算符 :: 就可以訪問
- 靜態(tài)成員函數(shù)只能訪問靜態(tài)成員數(shù)據(jù)、其他靜態(tài)成員函數(shù)和類外部的其他函數(shù)
- 靜態(tài)成員函數(shù)有一個(gè)類范圍,他們不能訪問類的 this 指針。您可以使用靜態(tài)成員函數(shù)來判斷類的某些對象是否已被創(chuàng)建
C++ 繼承
- 繼承代表了 is a 關(guān)系
- 一個(gè)類可以派生自多個(gè)類,可以從多個(gè)基類繼承數(shù)據(jù)和函數(shù)
- 如果未使用訪問修飾符 access-specifier,則默認(rèn)為 private
- 派生類可以訪問基類中所有的非私有成員
- 基類成員如果不想被派生類的成員函數(shù)訪問,則應(yīng)在基類中聲明為 private
- 一個(gè)派生類繼承了所有的基類方法,不繼承 1. 基類的構(gòu)造函數(shù)、析構(gòu)函數(shù)和拷貝構(gòu)造函數(shù); 2. 基類的重載運(yùn)算符; 3. 基類的友元函數(shù)
- 我們幾乎不使用 protected 或 private 繼承,通常使用 public 繼承
多繼承
class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>,…
{
<派生類類體>
};
重載運(yùn)算符
-
C++ 拷貝構(gòu)造函數(shù)
在創(chuàng)建對象時(shí),是使用同一類中之前創(chuàng)建的對象來初始化新創(chuàng)建的對象
如果在類中沒有定義拷貝構(gòu)造函數(shù),編譯器會自行定義一個(gè)
如果類帶有指針變量,并有動態(tài)內(nèi)存分配,則它必須有一個(gè)拷貝構(gòu)造函數(shù)
class Line
{
public:
int getLength( void );
Line( int len ); // 簡單的構(gòu)造函數(shù)
Line( const Line &obj); // 拷貝構(gòu)造函數(shù)
~Line(); // 析構(gòu)函數(shù)
private:
int *ptr;
};
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(int len)
{
cout << "調(diào)用構(gòu)造函數(shù)" << endl;
// 為指針分配內(nèi)存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷貝值
}
Line::~Line(void)
{
cout << "釋放內(nèi)存" << endl;
delete ptr;
}
C++ 友元函數(shù)
- 類的友元函數(shù)是定義在類外部,但有權(quán)訪問類的所有私有(private)成員和保護(hù)(protected)成員。
- 友元函數(shù)并不是成員函數(shù)
- 友元可以是一個(gè)函數(shù),該函數(shù)被稱為友元函數(shù);友元也可以是一個(gè)類,該類被稱為友元類,在這種情況下,整個(gè)類及其所有成員都是友元
- 如果要聲明函數(shù)為一個(gè)類的友元,需要在類定義中該函數(shù)原型前使用關(guān)鍵字 friend
C++ 內(nèi)聯(lián)函數(shù)
- 如果一個(gè)函數(shù)是內(nèi)聯(lián)的,那么在編譯時(shí),編譯器會把該函數(shù)的代碼副本放置在每個(gè)調(diào)用該函數(shù)的地方
- 對內(nèi)聯(lián)函數(shù)進(jìn)行任何修改,都需要重新編譯函數(shù)的所有客戶端,因?yàn)榫幾g器需要重新更換一次所有的代碼,否則將會繼續(xù)使用舊的函數(shù)
- 如果想把一個(gè)函數(shù)定義為內(nèi)聯(lián)函數(shù),則需要在函數(shù)名前面放置關(guān)鍵字 inline,在調(diào)用函數(shù)之前需要對函數(shù)進(jìn)行定義
- 如果已定義的函數(shù)多于一行,編譯器會忽略 inline 限定符
- 在類定義中的定義的函數(shù)都是內(nèi)聯(lián)函數(shù),即使沒有使用 inline 說明符
C++ this 指針
- C++ 中,每一個(gè)對象都能通過 this 指針來訪問自己的地址
- this 指針是所有成員函數(shù)的隱含參數(shù)。因此,在成員函數(shù)內(nèi)部,它可以用來指向調(diào)用對象
- 友元函數(shù)沒有 this 指針,因?yàn)橛言皇穷惖某蓡T。只有成員函數(shù)才有 this 指針
C++ 指向類的指針
- 訪問指向類的指針的成員,需要使用成員訪問運(yùn)算符 ->,就像訪問指向結(jié)構(gòu)的指針一樣
C++ 類的靜態(tài)成員
- 可以使用 static 關(guān)鍵字來把類成員定義為靜態(tài)的
- 聲明類的成員為靜態(tài)時(shí),這意味著無論創(chuàng)建多少個(gè)類的對象,靜態(tài)成員都只有一個(gè)副本
- 靜態(tài)成員在類的所有對象中是共享的
- 創(chuàng)建第一個(gè)對象時(shí),所有的靜態(tài)數(shù)據(jù)都會被初始化為零
- 不能把靜態(tài)成員的初始化放置在類的定義中,但是可以在類的外部通過使用范圍解析運(yùn)算符 :: 來重新聲明靜態(tài)變量從而對它進(jìn)行初始化
int Box::objectCount = 0;
靜態(tài)成員函數(shù)
- 如果把函數(shù)成員聲明為靜態(tài)的,就可以把函數(shù)與類的任何特定對象獨(dú)立開來。靜態(tài)成員函數(shù)即使在類對象不存在的情況下也能被調(diào)用,靜態(tài)函數(shù)只要使用類名加范圍解析運(yùn)算符 :: 就可以訪問
- 靜態(tài)成員函數(shù)只能訪問靜態(tài)成員數(shù)據(jù)、其他靜態(tài)成員函數(shù)和類外部的其他函數(shù)
- 靜態(tài)成員函數(shù)有一個(gè)類范圍,他們不能訪問類的 this 指針。您可以使用靜態(tài)成員函數(shù)來判斷類的某些對象是否已被創(chuàng)建
C++ 繼承
- 繼承代表了 is a 關(guān)系
- 一個(gè)類可以派生自多個(gè)類,可以從多個(gè)基類繼承數(shù)據(jù)和函數(shù)
- 如果未使用訪問修飾符 access-specifier,則默認(rèn)為 private
- 派生類可以訪問基類中所有的非私有成員
- 基類成員如果不想被派生類的成員函數(shù)訪問,則應(yīng)在基類中聲明為 private
- 一個(gè)派生類繼承了所有的基類方法,不繼承 1. 基類的構(gòu)造函數(shù)、析構(gòu)函數(shù)和拷貝構(gòu)造函數(shù); 2. 基類的重載運(yùn)算符; 3. 基類的友元函數(shù)
- 我們幾乎不使用 protected 或 private 繼承,通常使用 public 繼承
多繼承
class <派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>,…
{
<派生類類體>
};
重載運(yùn)算符
- 在同一作用域中的某個(gè)函數(shù)和運(yùn)算符指定多個(gè)定義,分別稱為函數(shù)重載和運(yùn)算符重載
- 重載聲明是指一個(gè)與之前已經(jīng)在該作用域內(nèi)聲明過的函數(shù)或方法具有相同名稱的聲明,但是它們的參數(shù)列表和定義(實(shí)現(xiàn))不相同
- 調(diào)用一個(gè)重載函數(shù)或重載運(yùn)算符時(shí),編譯器通過把您所使用的參數(shù)類型與定義中的參數(shù)類型進(jìn)行比較,決定選用最合適的定義。選擇最合適的重載函數(shù)或重載運(yùn)算符的過程,稱為重載決策
- 在同一個(gè)作用域內(nèi),可以聲明幾個(gè)功能類似的同名函數(shù),但是這些同名函數(shù)的形式參數(shù)(指參數(shù)的個(gè)數(shù)、類型或者順序)必須不同。您不能僅通過返回類型的不同來重載函數(shù)
- 重載的運(yùn)算符是帶有特殊名稱的函數(shù),函數(shù)名是由關(guān)鍵字 operator 和其后要重載的運(yùn)算符符號構(gòu)成的
C++ 多態(tài)
- 當(dāng)類之間存在層次結(jié)構(gòu),并且類之間是通過繼承關(guān)聯(lián)時(shí),就會用到多態(tài)
- C++ 多態(tài)意味著調(diào)用成員函數(shù)時(shí),會根據(jù)調(diào)用函數(shù)的對象的類型來執(zhí)行不同的函數(shù)
class Shape {
int area()
{
cout << "Parent class area :" <<endl;
}
};
class Rectangle: public Shape{
int area ()
{
cout << "Rectangle class area :" <<endl;
}
};
class Triangle: public Shape{
int area ()
{
cout << "Triangle class area :" <<endl;
}
};
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// 存儲矩形的地址
shape = &rec;
// 調(diào)用矩形的求面積函數(shù) area
shape->area();
// 存儲三角形的地址
shape = &tri;
// 調(diào)用三角形的求面積函數(shù) area
shape->area();
output:
Parent class area
Parent class area
- 調(diào)用函數(shù) area() 被編譯器設(shè)置為基類中的版本,這就是所謂的靜態(tài)多態(tài),或靜態(tài)鏈接
- 函數(shù)調(diào)用在程序執(zhí)行前就準(zhǔn)備好了。有時(shí)候這也被稱為早綁定,因?yàn)?area() 函數(shù)在程序編譯期間就已經(jīng)設(shè)置好了
- 在 Shape 類中,area() 的聲明前放置關(guān)鍵字 virtual
- 此時(shí),編譯器看的是指針的內(nèi)容,而不是它的類型
- 有了多態(tài),您可以有多個(gè)不同的類,都帶有同一個(gè)名稱但具有不同實(shí)現(xiàn)的函數(shù),函數(shù)的參數(shù)甚至可以是相同的
虛函數(shù)
- 在基類中使用關(guān)鍵字 virtual 聲明的函數(shù)
- 在派生類中重新定義基類中定義的虛函數(shù)時(shí),會告訴編譯器不要靜態(tài)鏈接到該函數(shù)
- 在程序中任意點(diǎn)可以根據(jù)所調(diào)用的對象類型來選擇調(diào)用的函數(shù),這種操作被稱為動態(tài)鏈接,或后期綁定
- 想要在基類中定義虛函數(shù),但是您在基類中又不能對虛函數(shù)給出有意義的實(shí)現(xiàn),這個(gè)時(shí)候就會用到純虛函數(shù)
-
virtual int area() = 0;= 0 告訴編譯器,函數(shù)沒有主體,上面的虛函數(shù)是純虛函數(shù)
C++ 數(shù)據(jù)抽象
- 數(shù)據(jù)抽象是指,只向外界提供關(guān)鍵信息,并隱藏其后臺的實(shí)現(xiàn)細(xì)節(jié),即只表現(xiàn)必要的信息而不呈現(xiàn)細(xì)節(jié)
- 依賴于接口和實(shí)現(xiàn)分離的編程(設(shè)計(jì))技術(shù)
- 在 C++ 中,我們使用訪問標(biāo)簽來定義類的抽象接口,一個(gè)類可以包含零個(gè)或多個(gè)訪問標(biāo)簽
- 使用公共標(biāo)簽定義的成員都可以訪問該程序的所有部分
- 一個(gè)類型的數(shù)據(jù)抽象視圖是由它的公共成員來定義的
- 使用私有標(biāo)簽定義的成員無法訪問到使用類的代碼,私有部分對使用類型的代碼隱藏了實(shí)現(xiàn)細(xì)節(jié)
- 訪問標(biāo)簽出現(xiàn)的頻率沒有限制,每個(gè)訪問標(biāo)簽指定了緊隨其后的成員定義的訪問級別
- 指定的訪問級別會一直有效,直到遇到下一個(gè)訪問標(biāo)簽或者遇到類主體的關(guān)閉右括號為止
兩個(gè)重要的優(yōu)勢:
- 類的內(nèi)部受到保護(hù),不會因無意的用戶級錯(cuò)誤導(dǎo)致對象狀態(tài)受損
- 類實(shí)現(xiàn)可能隨著時(shí)間的推移而發(fā)生變化,以便應(yīng)對不斷變化的需求,或者應(yīng)對那些要求不改變用戶級代碼的錯(cuò)誤報(bào)告
- 抽象把代碼分離為接口和實(shí)現(xiàn)。所以在設(shè)計(jì)組件時(shí),必須保持接口獨(dú)立于實(shí)現(xiàn),這樣,如果改變底層實(shí)現(xiàn),接口也將保持不變
C++ 數(shù)據(jù)封裝
- 封裝是面向?qū)ο缶幊讨械陌褦?shù)據(jù)和操作數(shù)據(jù)的函數(shù)綁定在一起的一個(gè)概念
- 數(shù)據(jù)封裝引申出了另一個(gè)重要的 OOP 概念,即數(shù)據(jù)隱藏
- 數(shù)據(jù)封裝是一種把數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)捆綁在一起的機(jī)制,數(shù)據(jù)抽象是一種僅向用戶暴露接口而把具體的實(shí)現(xiàn)細(xì)節(jié)隱藏起來的機(jī)制
- 通常情況下,我們都會設(shè)置類成員狀態(tài)為私有(private),除非我們真的需要將其暴露,這樣才能保證良好的封裝性
- C++中, 虛函數(shù)可以為private, 并且可以被子類覆蓋,但子類不能調(diào)用父類的private虛函數(shù)。虛函數(shù)的重載性和它聲明的權(quán)限無關(guān)
- 一個(gè)成員函數(shù)被定義為private屬性,標(biāo)志著其只能被當(dāng)前類的其他成員函數(shù)(或友元函數(shù))所訪問。而virtual修飾符則強(qiáng)調(diào)父類的成員函數(shù)可以在子類中被重寫,因?yàn)橹貙懼畷r(shí)并沒有與父類發(fā)生任何的調(diào)用關(guān)系,故而重寫是被允許的
- 被virtual修飾的成員函數(shù),不論他們是private、protect或是public的,都會被統(tǒng)一的放置到虛函數(shù)表中
- 對父類進(jìn)行派生時(shí),子類會繼承到擁有相同偏移地址的虛函數(shù)表(相同偏移地址指,各虛函數(shù)相對于VPTR指針的偏移),則子類就會被允許對這些虛函數(shù)進(jìn)行重載
- 重載時(shí)可以給重載函數(shù)定義新的屬性,例如public,其只標(biāo)志著該重載函數(shù)在該子類中的訪問屬性為public,和父類的private屬性沒有任何關(guān)系
- 純虛函數(shù)可以設(shè)計(jì)成私有的,不過這樣不允許在本類之外的非友元函數(shù)中直接調(diào)用它
- 子類中只有覆蓋這種純虛函數(shù)的義務(wù),卻沒有調(diào)用它的權(quán)利
C++ 接口(抽象類)
- 接口描述了類的行為和功能,而不需要完成類的特定實(shí)現(xiàn)
- C++ 接口是使用抽象類來實(shí)現(xiàn)的
- 如果類中至少有一個(gè)函數(shù)被聲明為純虛函數(shù),則這個(gè)類就是抽象類
- 設(shè)計(jì)抽象類的目的,是為了給其他類提供一個(gè)可以繼承的適當(dāng)?shù)幕?/li>
- 抽象類不能被用于實(shí)例化對象,它只能作為接口使用,如果試圖實(shí)例化一個(gè)抽象類的對象,會導(dǎo)致編譯錯(cuò)誤
- 如果一個(gè) ABC 的子類需要被實(shí)例化,則必須實(shí)現(xiàn)每個(gè)虛函數(shù),這也意味著 C++ 支持使用 ABC 聲明接口。如果沒有在派生類中重寫純虛函數(shù),就嘗試實(shí)例化該類的對象,會導(dǎo)致編譯錯(cuò)誤
- 面向?qū)ο蟮南到y(tǒng)可能會使用一個(gè)抽象基類為所有的外部應(yīng)用程序提供一個(gè)適當(dāng)?shù)?、通用的、?biāo)準(zhǔn)化的接口
- 派生類通過繼承抽象基類,就把所有類似的操作都繼承下來
C++ 文件和流
從文件讀取流和向文件寫入流。這就需要用到 C++ 中另一個(gè)標(biāo)準(zhǔn)庫 fstream,它定義了三個(gè)新的數(shù)據(jù)類型
- ofstream : 輸出文件流,用于創(chuàng)建文件并向文件寫入信息
- ifstream : 輸入文件流,用于從文件讀取信息
- fstream : 文件流,且同時(shí)具有 ofstream 和 ifstream 兩種功能,這意味著它可以創(chuàng)建文件,向文件寫入信息,從文件讀取信息
在 C++ 中進(jìn)行文件處理,必須在 C++ 源代碼文件中包含頭文件 <iostream> 和 <fstream>
打開文件
- 從文件讀取信息或者向文件寫入信息之前,必須先打開文件
- ofstream 和 fstream 對象都可以用來打開文件進(jìn)行寫操作,如果只需要打開文件進(jìn)行讀操作,則使用 ifstream 對象
- open() 成員函數(shù)的第一參數(shù)指定要打開的文件的名稱和位置,第二個(gè)參數(shù)定義文件被打開的模式
- ios::app 追加模式,所有寫入都追加到文件末尾
- ios::ate 文件打開后定位到文件末尾
- ios::in 打開文件用于讀取
- ios::out 打開文件用于寫入
- ios::trunc 如果該文件已經(jīng)存在,其內(nèi)容將在打開文件之前被截?cái)?,即把文件長度設(shè)為 0
- 可以把以上兩種或兩種以上的模式結(jié)合使用,
ios::out | ios::truncios::out | ios::in - C++ 程序終止時(shí),它會自動關(guān)閉刷新所有流,釋放所有分配的內(nèi)存,并關(guān)閉所有打開的文件
- 我們使用流插入運(yùn)算符( << )向文件寫入信息,就像使用該運(yùn)算符輸出信息到屏幕上一樣。唯一不同的是,在這里您使用的是 ofstream 或 fstream 對象,而不是 cout 對象
- 我們使用流提取運(yùn)算符( >> )從文件讀取信息,就像使用該運(yùn)算符從鍵盤輸入信息一樣。唯一不同的是,在這里您使用的是 ifstream 或 fstream 對象,而不是 cin 對象
- cin.getline()函數(shù)從外部讀取一行
- cin.ignore() 函數(shù)會忽略掉之前讀語句留下的多余字符
- 關(guān)于 istream 的 seekg("seek get")和關(guān)于 ostream 的 seekp("seek put")
- 參數(shù)通常是一個(gè)長整型。第二個(gè)參數(shù)可以用于指定查找方向。查找方向可以是 ios::beg(默認(rèn)的,從流的開頭開始定位),也可以是 ios::cur(從流的當(dāng)前位置開始定位),也可以是 ios::end(從流的末尾開始定位)
- 文件位置指針是一個(gè)整數(shù)值,指定了從文件的起始位置到指針?biāo)谖恢玫淖止?jié)數(shù)
C++ 異常處理
- 異常提供了一種轉(zhuǎn)移程序控制權(quán)的方式
- C++ 異常處理涉及到三個(gè)關(guān)鍵字:try、catch、throw