1. 程式人生 > >C++ 基類指標和派生類指標之間的轉換

C++ 基類指標和派生類指標之間的轉換

函式過載、函式隱藏、函式覆蓋

函式過載只會發生在同作用域中(或同一個類中),函式名稱相同,但引數型別或引數個數不同。 函式過載不能通過函式的返回型別來區分,因為在函式返回之前我們並不知道函式的返回型別。

函式隱藏和函式覆蓋只會發生在基類和派生類之間

函式隱藏是指派生類中函式與基類中的函式同名,但是這個函式在基類中並沒有被定義為虛擬函式,這種情況就是函式的隱藏。 所謂隱藏是指使用常規的呼叫方法,派生類物件訪問這個函式時,會優先訪問派生類中的這個函式,基類中的這個函式對派生類物件來說是隱藏起來的。 但是隱藏並不意味這不存在或完全不可訪問。通過 b->Base::func()訪問基類中被隱藏的函式。

函式覆蓋特指由基類中定義的虛擬函式引發的一種多型現象。在某基類中宣告為 virtual 並在一個或多個派生類中被重新定義的成員函式,用法格式為:virtual 函式返回型別 函式名(引數表) {函式體};實現多型性,通過指向派生類的基類指標或引用,訪問派生類中同名覆蓋成員函式。

函式覆蓋的條件:

  • 1: 基類中的成員函式被virtual關鍵字宣告為虛擬函式;
  • 2:派生類中該函式必須和基類中函式的名稱、引數型別和個數等完全一致;
  • 3:將派生類的物件賦給基類指標或者引用,實現多型。

函式覆蓋(多型)實現了一種基類訪問(不同)派生類的方法。我們把它稱為基類的逆襲。

基類指標和派生類指標之間的轉換

1. 基類指標指向基類物件、派生類指標指向派生類物件 這種情況是常用的,只需要通過對應類的指標直接呼叫對應類的功能就可以了。

#include<iostream>
using namespace std;

class Father{
public:    
    void print()
    {
        printf("Father's function!");
    }
};

class Son:public Father
{
public:
    void print()
    {
        printf("Son's function!");
    }
};

int main()
{
    Father f1;
    Son s1;

    Father* f = &f1;
    Son* s = &s1;

    f->print();
    cout<<endl<<endl;
    s->print();
}

2. 基類指標指向派生類物件

這種情況是允許的,通過定義一個基類指標和一個派生類物件,把基類指標指向派生類物件,但是需要注意,通常情況這時的指標呼叫的是基類的成員函式。分四種情況:

    一、 函式在基類和派生類中都存在

這時通過“指向派生類物件的基類指標”呼叫成員函式,呼叫的是基類的成員函式

    Father f1;     Son s1;

    Father* f = &s1;     f->print();  //呼叫的是基類成員函式

    二、函式在基類中不存在,在派生類中存在

由於呼叫的還是基類中的成員函式,試圖通過基類指標呼叫派生類才有的成員函式,則編譯器會報錯。

error C2039: “xxx”: 不是“Father”的成員

      三、 將基類指標強制轉換為派生類指標

這種是向下的強制型別轉換,轉換之後“指向派生類的基類指標”就可以訪問派生類的成員函式:

    Son s1;     Father* f = &s1;     Son *s = (Son*)f;     s->print1(); //呼叫派生類成員函式

但是這種強制轉換操作是一種潛在的危險操作

      四、基類中存在虛擬函式的情況

如果基類中的成員函式被定義為虛擬函式,並且在派生類中也實現了該函式,則通過“指向派生類的基類指標” 訪問虛擬函式,訪問的是派生類中的實現。允許“基類指標指向派生類”這個操作,最大的意義也就在此,通過虛擬函式和函式覆蓋,實現了“多型”(指向不同的派生類,實現不同功能)。

    Father f1;     Son s1;

    Father* f = &s1;     f->print();   //呼叫派生類成員函式

3. 派生類指標指向基類物件

會產生編譯錯誤。基類物件無法被當作派生類物件,派生類中可能具有隻有派生類才有的成員或成員函式。 即便是使用強制轉換,將派生類指標強制轉換成基類指標,通過這個“強制指向基類的派生類指標”訪問的函式依然是派生類的成員函式。

    Father f1;     Son s1;

    Son* s=&s1;     Father* f = (Father*) s;

    f->print();  //呼叫派生類成員函式

綜上,可以通過基類指標訪問派生類方法(強制轉換和虛擬函式),不存在通過派生類指標呼叫基類成員函式的方法(即便是強制轉換)。