之前C++學得就不太扎實,正好看到《VC++深入詳解》第二章對C++進行了一個簡單總結,故整理回顧之。
例如下面的程序就會報錯,因為x和y都是默認的私有成員,只有在類的內部才能進行訪問
5 5
那麼為什麼輸出不是10, 10呢,因為在input函數中,point類的成員變量x和y都是不可見的。並不是說成員變量在成員函數中不可見,而是如果成員函數中定義了和成員變量相同的變量,則成員變量在該成員函數中不可見,在下一個例子中可以說明這一點。
5 10
在這個例子中,從輸出x=5,y=10可以看出:成員變量y在成員函數input中就是可見的, 成員變量x在成員函數input中是不可見的。
那麼該如何利用成員函數給成員變量賦值呢,可以用下面兩個方法:
10 10
所以我們可以這樣寫這個程序:
10 10
先看下面這個例子:
animal construct fish construct fish destruct animal destruct
可以看出,在聲明子類對象時,父類的構造函數先運行,然後子類的構造函數,在對象聲明周期結束時,子類的析構函數先運行,然後是父類的析構函數。
animal breathe
fish bubble
為什麼例一的輸出結果為“animal breathe”而不是“fish bubble”呢?
因為我們將fish類的對象fh的地址賦值給pAn時,C++編譯器進行了類型轉換,此時C++編譯器認為變量pAn保存的就是animal對象的地址。 fish類對象所占的內存圖如下所示,它分為兩部分。用virtual關鍵字申明的函數叫做虛函數。
對於例二輸出為“fish bubble”,這就是C++的多態性。當C++編譯器在編譯的時候,發現animal類的breathe()是虛函數,這時C++就會采用遲綁定技術。也就是編譯時不確定具體調用的函數,而是在運行時,依據對象的類型(在程序中,我們傳遞的是fish類對象的地址)來確認調用的是哪一個函數,這種能力叫做C++的多態性。我們沒有在breathe()函數前加virtual關鍵字時,C++編譯器在編譯時就確定了哪個函數被調用,這叫做早期綁定。class animal { public: void eat() { cout << "animal eat" << endl; } void sleep() { cout << "animal sleep" << endl; } virtual void breathe() = 0; };
構成函數覆蓋的條件:
基類函數必須是虛函數; 發生覆蓋的兩個函數分別位於基類和派生類中; 函數名稱與參數列表必須完全相同。例:
class animal { public: ... virtual void breathe() { cout << "animal breathe" << endl; } ... }; class fish:public animal { public: ... void breathe() { cout << "fish bubble" << endl; } ... };
上述例子中,fish類中的breathe()函數就實現了對animal類中breathe函數的覆蓋。fish類中的breathe()函數仍然是虛函數。
class animal { public: ... void breathe() { cout << "animal breathe" << endl; } ... }; class fish:public animal { public: ... void breathe() { cout << "fish bubble" << endl; } ... };
兩種函數隱藏的情況:
派生類的函數與基類的函數完全相同(函數名和參數列表都相同),只是基類的函數沒有使用virtual關鍵字。此時基類的函數將被隱藏,而不是覆蓋。 派生類的函數與基類的函數同名,但參數列表不同,在這種情況下,不管基類的函數聲明是否有virtual關鍵字,基類的函數都將被隱藏(覆蓋的條件是函數名和參數列表都相同,且基類中函數用virtual關鍵字修飾)。例二
class Base { public: virtual void fn(); }; class Derived:public Base { public: void fn(int); }; class Derived2:public Derived { public: void fn(); };
引用就是一個變量的別名。它需要用另一個變量或對象來初始化自身。引用就像一個人的外號一樣。
例一//下面的代碼生命了一個引用b,並用變量a進行了初始化 int a = 5; int &b = a;
用&表示申明一個引用,引用必須在申明時進行初始化。
int a = 5; int& b = a ; int c = 3; b = c;
上例中,並不是將b變成c的引用,而是給b賦值,此時b和a的值都變成了3。
例二#includeusing namespace std; //change函數主要用來交換a和b的值 void change(int& a, int& b); int main(void) { int x = 5; int y = 3; cout << "original x = " << x << endl; cout << "original y = " << y << endl; change(x, y); //此處如果用指針傳遞,則調用change(&x, &y),這樣很容易讓人迷惑,不知道交換的是x和y的值,還是x和y的地址?此處使用引用,可讀性就比指針要好 cout << "changed x = " << x << endl; cout << "changed y = " << y << endl; return 0; } /* change()函數中采用了一個巧妙的算法來實現了a和b值的互換 */ void change(int& a, int& b) { a = a+b; b = a-b; a = a-b; }
上述例子中,不能將函數定義成void change(int a, int b)。 如果定義成這樣,在調用完成之後,x還是等於原來的x,y還是等於原來的y,因為函數中的a=x,b=y,而x!=a,y!=b。使用引用就不同了,使用引用後,a和x,b和y事實上是相同的,因為他們在內存中占用的是同一個內存單元。
在設計一個類的時候,通常是將類的定義及類成員函數的聲明放到頭文件(即.h文件)中,將類中成員函數的實現放到源文件(即.cpp)中。對於main()函數,我們則單獨把它放到main.cpp文件中。
對於頭文件重復包含的情況,如main.cpp包含了animal.h文件和fish.h文件,而fish.h文件又包含了animal.h文件。這樣就會出現頭文件重復包含了,編譯報錯:‘class’ type redefinition。解決方法如下,在每一個頭文件中,都使用條件預處理指令,如下:
#ifndef _ANIMAL_H_ #define _ANIMAL_H_ class animal { public: animal(); ~animal(); void eat(); void sleep(); virtual void breathe(); }; #endif