面對物件過程程式設計的主要目的之一是提供可重用的**塊。當專案十分龐大時,對已通過測試的**進行重用比重新編寫**更能提供效率,節省時間。
c++通過擴充套件和修改類來提高**的重用性,這種方法叫類繼承。從已有的類(基類)派生出新的類(派生類),派生類繼承了基類的所有特性(成員變數、方法等),並且還可以在此基礎上新增特有的特性完成對基類的拓展。這種繼承的方式比重新設計乙個與基類相似的類更方便,提高寫**的效率。
class
animal
void
getsize()
virtual
void
cry(
)virtual
~animal()
private
:int size;
int foot;
}
這是乙個名為animal的基類,然後需要寫乙個名為dog的類,而這個dog類擁有animal類的所有特性並且在此加上了成員變數tail以及對cry()方法進行修改。因此,可以通過繼承animal來獲得其特性。
class
dog:
public animal
virtual
void
cry(
)void run()
virtual
~dog()
private
:int tail;
}
這裡dog類對方法cry()進行了改寫,並增加了方法run()和變數tail,其他方法和變數則完全繼承於animal類。
這樣只需要對修改的方法進行重新設計,並增添新的成員變數或方法,從而免去了對重複**的編寫。
animal
(int size,
int foot)
:size
(size)
,foot
(foot)
dog
(int size,
int foot,
int tail)
:animal
(size,foot)
這裡可以看出派生類的建構函式與基類的建構函式的有所不同。這是由於建立派生類物件時,程式需要首先建立基類物件。
下面執行一段測試用例
#include
#include
"animal.h"
intmain()
執行結果
從這裡可以看出,animal的建構函式被呼叫了2次,然而測試用例中只對初始化了乙個animal物件。程式在建立乙個dog物件的時候也建立了乙個animal物件。派生類所繼承的成員變數的初始化是通過建立乙個基類物件來進行的。
而在派生類的析構函式被呼叫後,也會呼叫一次基類的析構函式以將建立派生類物件的同時建立的基類物件析構掉。
由於派生類繼承了基類的所有特性,因此派生類 is a 基類。如在程式中,dog繼承了animal,那麼dog就是乙個animal。is-a關係是單向的,因此並不存在基類 is a 派生類的關係。基於這種is-a關係,可以用指向基類型別的指標或基類型別的引用來指向乙個派生類物件。
如:
dog a(2,3,1);
animal * b = a;
由此可以得出一種用乙個指向基類的陣列指標來儲存基類和派生類物件的應用
如:
dog d1(2,3,1);
dog d2(2,3,1);
dog d3(2,3,1);
animal a1(2,3);
animal a2(2,3);
animal a3(2,3);
animal * a[6]
;a[0]
=&d1;a[1
]=&d2;a[2
]=&d3;a[3
]=&a1;a[4
]=&a2;a[5
]=&a3;
virtual關鍵字宣告了乙個函式為虛函式。當使用乙個基類指標或基類引用指向乙個派生類物件時,通過這個指標或引用來呼叫重寫函式如cry(),虛函式將指明呼叫的是dog類(派生類)的cry()而不是animal類(基類)的cry()。若派生類中沒有對虛函式進行重定義,則呼叫基類的版本。
dog a(2,3,1);
animal * b = a;
b->
cry();
//呼叫dog::cry()
而如果cry()函式沒有用virtual標記,則通過基類指標或基類應用來呼叫重寫函式時,將被呼叫的是基類的函式。
dog a(2,3,1);
animal * b = a;
b->
cry();
//呼叫animal::cry()
如果是通過物件來呼叫重寫函式則不會出現上述的情況。
dog a(2,3,1);
animal b(2,1);
a->
cry();
//呼叫dog::cry()
b->
cry();
//呼叫animal::cry()
因此,一般都會用virtual關鍵字標記重寫函式,以避免上述的第二種情況。
而析構函式一般都會標上virtual,因為如果析構函式中涉及到釋放記憶體的操作時,沒有標上virtual,可能會出現析構函式呼叫順序錯亂而引發記憶體釋放不完全或者重複釋放同一塊記憶體的錯誤。
擁有乙個純虛函式的基類就稱為抽象基類。抽象基類與基類的最大不同在於,抽象基類不能建立物件,因為抽象基類的純虛函式可以只提供介面而不實現。抽象基類就相當於乙個基礎模板,其派生類將繼承他的所有特性並實現那些未實現的介面,按需要進行擴充套件,成為乙個完整的類。
純虛函式相比虛函式的不同在於,在函式宣告的末尾加上了=0,以及可以只宣告和不定義其實現。
virtual
void
cry()=
0;
對於在類外,protected與private相似,只能通過public成員來對protected成員進行訪問。
而在類內,派生類可以直接訪問基類的protected成員
public繼承:所有成員訪問許可權不變
protected繼承:將public成員變為protected成員,private成員不變
private繼承:將所有成員訪問許可權改為private
C語言個人學習筆記
在匯程式設計序中,乙個函式的開頭常常見到以下 8048cbd 83 ec 18 sub 0x18,esp這句話的目的是分配棧幀。棧幀中存放的是程式中的區域性變數。在windows的編譯器cl.exe中這些棧幀中的值最初會被初始化為0xcc,這也是為什麼vc 在陣列越界或訪問未賦值的記憶體時列印 燙燙...
繼承的本質的個人學習筆記
對繼承的本質的一些理解,看了些文章和部落格,總結成自己的文字加深一下理解,可能表達和理解的不是很透徹,請帶著批判的眼光閱讀 先附上大神們的文章 new建立物件時,遞迴查詢父類,分配記憶體,最先建立的應該是object。物件是在執行時建立的,方法是在編譯時建立,物件欄位和方法的建立和呼叫遵循執行就近原...
java基礎 個人學習筆記 C
12.class valuetest1 intnum 10 for strings student system.out.println 方法呼叫前 t student 0 范冰冰 changevalue student system.out.println 方法呼叫後 t student 0 周杰...