用了這麼久C++,到頭來才發現很多基礎的東西搞的不夠透徹,藉此機會鞏固一下。
首先看一看C++的三大特性:封裝性、繼承性、多態性
1.封裝性
封裝性使得程式更加結構化,並且可以通過存取權限保護資料成員。
C++中結構體和類實際上是同一種東西,在結構體中也可以定義方法,也可以使用許可權標識符。兩者有兩個不同點:
(1)關鍵字不同,一個是struct,一個是class
(2)成員的預設存取權限不同,結構體的預設存取權限是public,而類的預設存取權限是private
2.繼承性
用":"表示繼承關係,繼承方式有三種,預設為protected繼承方式:
(1)public:父類的public、protected許可權的成員在子類中的存取權限保持不變
(2)protected:父類的public、protected許可權的成員在子類中的存取權限均變為protected
(3)private:父類的public、protected許可權的成員在子類中的存取權限均變為private
無論哪種繼承方式,父類的private方法都無法被子類訪問
3.多態性
在寫程式的時候,一個父類會有很多子類,現在如果要寫一個通用的函數,參數為該類的對象的指標,那麼很顯然,我們不希望為每個子類都寫一個這樣的函數,那樣就太麻煩了。能不能唯寫一個關於父類的函數,而傳遞參數時傳遞子類的對象,讓該函數自己判斷傳入對象的類型,然後調用正確的方法呢?答案是可以的,這就是“遲綁定”技術。要實現“遲綁定”,只需要在父類中把需要調用的函式宣告為virtual就可以了。
比如下面這個函數:
void fn(Animal *pAn)
{
pAn->eat();
}
Fish是Animal類的一個子類,eat()是Animal中聲明的一個虛函數,如果傳遞Fish對象給函數fn,那麼在程式運行時,將會把eat()綁定到Fish對象的eat()方法上,如果Fish類中沒有覆蓋eat()方法,那麼就會綁定到父類Animal的eat()方法。
注意:
(1)如果沒有聲明為virtual方法,那麼fn函數將把Fish對象強制轉換為Animal對象,這樣調用的就是Animal對象的eat()方法,而不是調用Fish對象的eat()方法了。
(2)可以只在父類中聲明函數,而不實現,這樣該函數就成為一個純虛函數:
virtual void eat() = 0;
而父類就變成一個抽象類別,不能執行個體化對象,子類必須實現純虛方法。
下面再看看C++中的一些其他的重要的特性。
4.函數重載
如果要實現一個通用的加法函數add,在C語言中必須針對不同的資料類型構造不同的函數,如:
addInt(int x, int y);
addFloat(float x, float y);
...
這樣就需要記憶很多的函數名,造成了函數介面的不統一。在C++中允許不改變函數名,只改變參數類型,來實現函數的重載,有效解決了這個問題:
add(int x, int y);
add(float x, float y);
...
重載與傳回值無關,函數名相同但傳回值不同的兩個函數是無法通過編譯的。
5.函數的覆蓋
這個要和函數重載區分開,函數的覆蓋指的是在子類中重寫父類中定義的方法。
6.引用與指標
引用就是一個變數的別名,指的是同一塊記憶體地區,本身不佔記憶體空間;而指標存放的是變數的地址,本身要佔據4個位元組的空間。
引用必須在定義時就賦值,並且以後不能更改所引用的變數;而指標可以存放任意變數的地址,可以隨時改變。
在傳遞函數參數時,有時候參考型別比指標類型看起來更直觀,更能表現函數所表達的意思,如:
void swap(int *pA, int *pB)
{
int temp;
temp = *pA;
*pA = *pB;
*pB = temp;
}
調用:swap(&x, &y);//需要傳遞變數的地址
void swap(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}
調用:swap(x, y);//直接傳遞變數的引用
7.建構函式和解構函式
只需要注意一下父類和子類建構函式和解構函式的調用順序:
分配記憶體空間時先調用父類的建構函式,再調用子類的建構函式
釋放記憶體空間時先調用子類的解構函式,再調用父類的解構函式
8.this指標
一個類的所有對象所調用的成員函數都是同一程式碼片段,不同對象調用成員函數時將傳遞一個隱含的形參:this指標,對所有資料成員的訪問都隱式的被加上this->。