C++中的基類與派生類

派生類的繼承方式總結:

繼承方式 說明
public 基類的public和protected的成員被派生類繼承後,保持原來的狀態
private 基類的public和protected的成員被派生類繼承後,變成派生類的private成員
protected

基類的public和protected的成員被派生類繼承後,變成派生類的protected成員ios

注:不管何種繼承方式,基類的private成員都不能被派生類訪問。從上面的表中能夠看出,聲明爲public的方法和屬性能夠被隨意訪問;聲明爲protected的方法和屬性只能被類自己和其子類訪問;而聲明爲private的方法和屬性只能被當前類的對象訪問。    

1. 友元函數必須在類中進行聲明而在類外定義,聲明時須在函數返回類型前面加上關鍵字friend。友元函數雖不是類的成員函數,但它能夠訪問類中的私有和保護類型數據成員。  編程

2. 虛函數在從新定義時參數的個數和類型必須和基類中的虛函數徹底匹配,這一點和函數重載徹底不一樣。  數組

3. #include <文件名>和#include "文件名"  ide

  • 文件包含的兩種格式中,第一種格式用來包含那些由系統提供的並放在指定子目錄中的頭文件;而第二種格式用來包含那些由用戶本身定義的放在當前目錄或其餘目錄下的頭文件或其它源文件。  

4. 數組也能夠做爲函數的實參和形參,若數組元素做爲函數的實參,則其用法與變量相同。當數組名做爲函數的實參和形參時,傳遞的是數組的地址。當進行按值傳遞的時候,所進行的值傳送是單向的,即只能從實參傳向形參,而不能從形參傳回實參。形參的初值和實參相同,而形參的值發生改變後,實參並不變化,二者的終值是不一樣的。而當用數組名做爲函數參數進行傳遞時,因爲實際上實參和形參爲同一數組,所以當形參數組發生變化時,實參數組也隨之發生變化。  函數

注:實參數組與形參數組類型應一致,如不一致,結果將出錯;形參數組也能夠不指定大小,在定義數組時數組名後面跟一個空的方括號,爲了在被調用函數中處理數組元素的須要,能夠另設一個參數,傳遞數組元素的個數。如:int sum(int array[],int n);  spa

5. 重載、覆蓋和隱藏的區別?  指針

函數的重載是指C++容許多個同名的函數存在,但同名的各個函數的形參必須有區別:形參的個數不一樣,或者形參的個數相同,但參數類型有所不一樣。 code

覆蓋(Override)是指派生類中存在從新定義的函數,其函數名、參數列、返回值類型必須同父類中的相對應被覆蓋的函數嚴格一致,覆蓋函數和被覆蓋函數只有函數體 (花括號中的部分)不一樣,當派生類對象調用子類中該同名函數時會自動調用子類中的覆蓋版本,而不是父類中的被覆蓋函數版本,這種機制就叫作覆蓋。對象

下面咱們從成員函數的角度來說述重載和覆蓋的區別。blog

成員函數被重載的特徵有: 1)相同的範圍(在同一個類中);2) 函數名字相同;3) 參數不一樣;4) virtual關鍵字無關緊要。
覆蓋的特徵有: 1)不一樣的範圍(分別位於派生類與基類)2) 函數名字相同;3) 參數相同;4) 基類函數必須有virtual關鍵字。
隱藏是指派生類的函數屏蔽了與其同名的基類函數,規則以下 1)若是派生類的函數與基類的函數同名,可是參數不一樣。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。2) 若是派生類的函數與基類的函數同名,而且參數也相同,可是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。

好比,在下面的程序中:

複製代碼
#include  < iostream.h >
class  Base
{
public :
void  f( int  x){ cout  <<   " Base::f(int)  "   <<  x  <<  endl; }
void  f( float  x){ cout  <<   " Base::f(float)  "   <<  x  <<  endl; }
virtual   void  g( void ){ cout  <<   " Base::g(void) "   <<  endl;}
};

class  Derived :  public  Base
{
public :
virtual   void  g( void ){ cout  <<   " Derived::g(void) "   <<  endl;}
};

void  main( void )
{
Derived d;
Base 
* pb  =   & d;
pb
-> f( 42 );  //  運行結果: Base::f(int) 42
pb -> f( 3.14f );  //  運行結果: Base::f(float) 3.14
pb -> g();  //  運行結果: Derived::g(void)
}
複製代碼
 

函數Base::f(int)與Base::f(float)相互重載,而Base::g(void)被Derived::g(void)覆蓋。

好比,在下面的程序中:

複製代碼
#include  < iostream.h >

class  Base
{
public :
virtual   void  f( float  x){ cout  <<   " Base::f(float)  "   <<  x  <<  endl; }
void  g( float  x){ cout  <<   " Base::g(float)  "   <<  x  <<  endl; }
void  h( float  x){ cout  <<   " Base::h(float)  "   <<  x  <<  endl; }
};

class  Derived :  public  Base
{
public :
virtual   void  f( float  x){ cout  <<   " Derived::f(float)  "   <<  x  <<  endl; } // 被繼承以後,virtual 無關緊要,但最好有。繼承後,仍是虛函數。
void  g( int  x){ cout  <<   " Derived::g(int)  "   <<  x  <<  endl; }
void  h( float  x){ cout  <<   " Derived::h(float)  "   <<  x  <<  endl; }
using  Base::g; // 這句話是用來引用父類中被隱藏的部分的。
};
複製代碼
 

經過分析可得:

1) 函數Derived::f(float)覆蓋了Base::f(float)。

2) 函數Derived::g(int)隱藏了Base::g(float),注意,不是重載。

3) 函數Derived::h(float)隱藏了Base::h(float),而不是覆蓋。

看完前面的示例,可能你們還沒明白隱藏與覆蓋到底有什麼區別,由於咱們前面都是講的表面現象,怎樣的實現方式,屬於什麼狀況。下面咱們就要分析覆蓋與隱藏在應用中到底有什麼不一樣之處。在下面的程序中bp和dp指向同一地址,按理說運行結果應該是相同的,可事實並不是如此。

複製代碼
void  main( void )
{
Derived d;
Base 
* pb  =   & d;
Derived 
* pd  =   & d;
//  Good: behavior depends solely on type of the object
pb -> f( 3.14f );  // 運行結果: Derived::f(float) 3.14
pd -> f( 3.14f );  // 運行結果: Derived::f(float) 3.14
//  Bad : behavior depends on type of the pointer
pb -> g( 3.14f );  // 運行結果: Base::g(float) 3.14
pd -> g( 3.14f );  // 運行結果: Derived::g(int) 3
//  Bad : behavior depends on type of the pointer
pb -> h( 3.14f );  // 運行結果: Base::h(float) 3.14
pd -> h( 3.14f );  // 運行結果: Derived::h(float) 3.14
}
複製代碼
 

請你們注意,f()函數屬於覆蓋,而g()與h()屬於隱藏。從上面的運行結果,咱們能夠注意到在覆蓋中,用基類指針和派生類指針調用函數f() 時,系統都是執行的派生類函數f(),而非基類的f(),這樣實際上就是完成的「接口」功能。而在隱藏方式中,用基類指針和派生類指針調用函數f()時,系統會進行區分,基類指針調用時,系統執行基類的f(),而派生類指針調用時,系統「隱藏」了基類的f(),執行派生類的f(),這也就是「隱藏」的由來。

重載(overload):這個好理解,在同個space域同名的。參數必須不一樣,有關virtual無關.

覆蓋(override):同名字,同參數,有virtual,覆蓋好理解好比show()函數,A派生了B,若是B中的show()覆蓋了A中的show(),但B中仍然有兩個show(),而不論是A類指針也好,B類對象調用也好,都只能調用B類本身的那個show();而從A類繼承過來的show()函數真的就被覆蓋了,沒有了嗎? 答案是不對的.這時能夠在B類對象顯示的調用A類繼承過來的show();

程序代碼:

複製代碼
#include  < iostream >  
using   namespace  std; 

class  A 

public
virtual   void  show() 

cout 
<<  a  <<  endl; 


int  a; 
}; 

class  B: public  A 

public
void  show() 

A::show(); 
// 顯式地調用本身類中的 "由A類繼承過來的show()函數" ,像這種直接顯式指出某個類的某個函數時, 編譯器處理方式是這樣的: 首先在本身類中找有沒有A::show(),若是找到,調用.不在繼續在A類中找,若是找不到,則在顯式指出的那個類中(即A類)調用那個函數. 這裏固然是在B類中能夠找到A::show() ,由於基類中指出了這個函數是virtual函數. 

int  b; 
};

int  main() 

A a; 
a.a 
=   3
a.show(); 

B b; 
b.b 
=   4
b.show(); 
b.A::show(); 
// 顯示的調用本身類中的 "由A類繼承過來的show()函數"

return   0
}
複製代碼
 

總結:

通俗的講B類仍是有兩個show(),只是調用由A繼承過來的show()只能經過顯式的調用方法 [類名::virtual函數名] 而不論是基類A的指針 (B b; A *p = &b; p->show())或者派生類的對象(B b; b.show()),都只能調用B類的本身自己存在的show()函數

隱藏hide:

1:同名同參無virtual

2:同名不一樣參無論有無virtual

程序代碼:

複製代碼
class  A 

public
void  show() {};  // 編號1 
void  rose( int  a) {}   // 編號2 
}; 

class  B: public  A 

public
void  show() {};  // 編號3 
void  rose( int  a,  int  b) {};  // 編號4 
};
複製代碼
 

類B中的show()和rose()明顯是隱藏了類A的show()和rose() 隱藏的理解: B類中其實有兩個show(),兩個rose(); 但爲何不叫重載呢?你會這樣想,但我能夠告訴你,由於類B中的兩個show(),兩個rose(),不是均可以被B類的對象調用的.

編號1和編號2,在類B中哪怕存在,但只能經過類A的指針調用,而不能經過B類對象調用,如:

程序代碼:

* =   new  B; 
p
-> show(); 
p
-> rose( 3 ); 
p
-> rose( 3 , 5 );  // error
 

編號3和編程4,只能經過類B對象調用,而不能經過類A的指針調用,如:

程序代碼:

B b; 
b.show(); 
b.rose(
3 , 5 ); 
b.rose(
4 );  // error
 

6. 用參數列表能夠區分重載函數,爲何返回值卻不能區分重載函數?

好比說有兩個函數:int fun(); double fun(); 若是寫fun();那麼編譯器就不知道該調用誰了。函數調用結束後才能肯定返回值,但程序在調用函數時就須要明確知道調用哪個重載的函數,這先後矛盾。

7. 派生類與基類之間的關係

  • 派生類對象可使用基類的方法,條件是方法在基類中沒有被聲明爲私有的。基類指針能夠在不進行顯式類型轉換的狀況下指向派生類對象,基類引用能夠在不進行顯式類型轉換的狀況下引用派生類對象。不過,基類指針或引用只能用於調用基類方法,不能調用派生類的方法。一般C++要求引用和指針類型與賦給的類型匹配,但這一規則對繼承來講是個例外。不過這種例外只是單向的,不能夠將基類對象和地址賦給派生類引用和指針。

8. 靜態聯編和動態聯編

程序調用函數時,將使用哪一個可執行代碼塊呢?編譯器負責回答這個問題。將源代碼中的函數調用解釋爲執行特定的函數代碼塊被稱爲函數名聯編。在C語言中,這很是簡單,由於每一個函數名都對應一個不一樣的函數。在C++中,因爲函數重載的緣故,這項任務更復雜。編譯器必須查看函數參數以及函數名才能肯定使用哪一個函數。然而,C/C++編譯器能夠在編譯過程完成這種聯編。在編譯過程當中進行聯編被稱爲靜態聯編/綁定,又稱爲早期聯編/綁定。不過,虛函數使這項工做變得更困難。由於使用哪一個函數是不能在編譯時肯定的,由於編譯器不知道用戶將選擇哪一種類型的對象。因此,編譯器必須生成可以在程序運行時選擇正確的虛方法的代碼,這被稱爲動態聯編/綁定,又被稱爲晚期聯編/綁定