C++基類、派生類、虛函數的幾個知識點


1.盡管派生類中含有基類繼承來的成員,但派生類初始化這部分變量需要調用基類的構造函數。

 1 class A
 2 {
 3 private:
 4     int x;
 5     virtual void f(){cout<<"A f"<<endl;}
 6 public:
 7     A(/* args */){x=1;cout<<"A構造"<<endl;}
 8     ~A(){}
 9     friend void p(const A& a){cout<<a.x;}
10 };
11 class B:A{
12     public:
13     void f() override {cout<<"B f"<<endl;}
14     operator A()const {}
15 };
16 int main()
17 {
18     B x;
19     system("pause");
20     return 0;
21 }

 2.如果基類定義了一個靜態成員,則整個繼承體系中只存在該成員的唯一定義。並且不論基類派生出多少派生類,該靜態成員只存在唯一實例。

並且屬性也一致。基類中某靜態成員是public,派生類中也是public。如果基類中是private,那么派生類也無法調用該靜態成員。

 1 /* 基類的靜態成員 */
 2 
 3 #include<iostream>
 4 
 5 using namespace std;
 6 
 7 class base
 8 {
 9 public:
10     static int num;
11 
12     base()
13     {
14         num+=1;
15     }
16 
17     ~base()
18     {
19         num-=1;
20     }
21 };
22 
23 // 基類定義的靜態成員,被所有派生類共享 
24 // 起到計數器的作用,自身還有所有派生類對象的個數
25 // 遵循私有 公有 保護的繼承機制 訪問不同
26 // 可以通過父類  子類對象訪問 還可以通過父類  子類類名訪問
27 int base::num = 0;// 類的靜態成員
28 
29 class basenew : public base
30 {
31     
32 };
33 
34 class basenewx : public basenew
35 {
36     
37 };
38 
39 
40 void main()
41 {
42     basenew *p = new basenew[100];
43 
44     base *p1 = new base[40]
45 
46     basenewx *p2 = new basenewx[50];
47     
48     cout << p->num << endl;// 190
49     cout << p1->num << endl;// 190
50     cout << p2->num << endl;// 190
51 
52 
53     cin.get();
54 }

3.如果不想讓某個類被繼承,在類名后加final關鍵字。

 

final除了可以修飾類外,還可以修飾成員函數。還可以指明某個基類的虛函數不能被其派生類版本覆蓋,如下:

首先要明確覆蓋(override)與重載(overload)的定義,區別出什么是覆蓋和重載:

覆蓋就是派生類中虛成員函數覆蓋基類中同名且參數相同的成員函數。

 1 class A
 2 {
 3 public:
 4     A(/* args */){}
 5     virtual void f()final {}
 6     virtual ~A(){}
 7 };
 8 class B:public A{
 9     public:
10     void f(int x){}//重載(overload)
11     void f(){}  //覆蓋(override,非法,因為A中的f聲明了final)
12 };

 

 4.派生類對象是基類對象,派生類中包含有基類的成員。基類對象不是派生類對象,它不能包含派生類型的成員。

派生類可以向基類轉化(僅限指針和引用),基類不能向派生類轉化(畢竟派生類有多余的數據,基類沒法自己生成)

如果用派生類對象為一個基類對象初始化或者賦值,只有其中的基類部分會被拷貝/移動/賦值,它的派生類部分會被忽略。

 1 class A
 2 {
 3 private:
 4     int x;
 5 public:
 6     A(/* args */){x=1;cout<<"A構造"<<endl;}
 7     virtual ~A(){}
 8 };
 9 class B:public A{
10     
11 };
12 int main()
13 {
14     A x;
15     B y;
16     A* p=&y;//正確,將基類指針指向派生類變量,派生類指針隱式轉換為基類指針。
17     B* q=&x;//錯誤
18     system("pause");
19     return 0;
20 }

5.動態綁定(即dynamic bninding)只有當我們通過指針或者引用調用虛函數時才會發生。

由於繼承導致對象的指針和引用具有兩種不同的類型:靜態類型和動態類型。
靜態類型:指針或者是引用聲明時的類型。
動態類型:由實際指向的類型確定。

其中靜態類型編譯時就已經確定,動態類型只有到運行的時候才能知道。所以如果用普通類類型調用虛函數,編譯時就會把要調用的虛函數版本確定下來。

 1 class A
 2 {
 3 public:
 4     A(/* args */){}
 5     virtual void f(){cout<<"基類的f"<<endl;}
 6     virtual ~A(){}
 7 };
 8 class B:public A{
 9     public:
10     void f(){cout<<"派生類的f"<<endl;}
11 };
12 void F(A& p){
13     p.f();
14 }
15 int main()
16 {
17     A x;
18     B y;
19     F(x);
20     F(y);
21     system("pause");
22     return 0;
23 }

可以看到同一個函數F,因為調用不同類型實參,最終在內部調用了不同版本的f函數,這就是動態綁定。

 

 

如果F的形參改成普通類型,那么兩次都會調用基類的f函數,其中F(y)會把y隱式類型轉換為一個A類型的臨時變量傳給形參。

 

 

6.如果一個派生類虛函數需要調用其基類版本,但沒有使用“基類::”修飾,則運行時該調用會被解析為對其自身的調用,這將導致無限遞歸。

7.派生類中的虛函數可以在形參后加override,顯式告知編譯器該虛函數覆蓋了其基類版本。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM