1. 程式人生 > >C++函式的隱藏(遮蔽)、覆蓋(override,即重寫,多型的實現原理)、過載(overload)

C++函式的隱藏(遮蔽)、覆蓋(override,即重寫,多型的實現原理)、過載(overload)

零、前言

(1)函式過載發生在同一個類或頂層函式中,同名的函式而具有不同的引數列表
(2)函式覆蓋(重寫)發生在繼承層次中,該函式在父類中必須是virtual,而子類的該函式必須與父類有相同的引數列表
(3)函式**隱藏(遮蔽)**發生在繼承層次中,父類和子類同名的函式中,不屬於函式覆蓋的都屬於函式隱藏
參考:https://www.cnblogs.com/xhyzdai/archive/2012/11/30/2796196.html

一、關於遮蔽,我們需要注意什麼?

1、基類和派生類成員的名字一樣時會造成遮蔽
2、不管函式的引數如何,只要名字一樣就會造成遮蔽。換句話說,基類成員函式和派生類成員函式不會構成過載,如果派生類有同名函式,那麼就會遮蔽基類中的所有同名函式,不管它們的引數是否一樣。
3、即使派生類的成員(包括成員變數和成員函式)和基類中成員重名,造成遮蔽,仍然可以訪問基類的成員變數和成員函式,不過要加上類名和域解析符

。如:

Derived *p = new Derived;
p->Base::fun(); //使用指向派生類的指標p來呼叫基類Base中的函式fun;
cout << p->Base::i << endl; //輸出基類中的變數i;

二、驗證

2、1基類和派生類成員的名字一樣時會造成遮蔽

#include<iostream>
using namespace std;
//基類People
class People{
public:
    void show();
protected:
    char *m_name;
    int m_age;
}
; void People::show(){ cout<<"嗨,大家好,我叫"<<m_name<<",今年"<<m_age<<"歲"<<endl; } //派生類Student class Student: public People{ public: Student(char *name, int age, float score); public: void show(); //遮蔽基類的show() private: float m_score; }; Student::Student(char
*name, int age, float score){ m_name = name; m_age = age; m_score = score; } void Student::show(){ cout<<m_name<<"的年齡是"<<m_age<<",成績是"<<m_score<<endl; } int main(){ Student stu("小明", 16, 90.5); //使用的是派生類新增的成員函式,而不是從基類繼承的 stu.show(); //使用的是從基類繼承來的成員函式 stu.People::show(); return 0; }

關於第三點,即使派生類的成員(包括成員變數和成員函式)和基類中成員重名,造成遮蔽,仍然可以訪問基類的成員變數和成員函式,不過要加上類名和域解析符。 如上面例子中的 stu.People::show();

2.2不管成員函式的引數如何,只要名字一樣就會造成遮蔽

// 編譯器:code::blocks gcc 4.9.2
#include<iostream>
using namespace std;
//基類Base
class Base{
public:
    void func();
    void func(int);
};
void Base::func(){ cout<<"Base::func()"<<endl; }
void Base::func(int a){ cout<<"Base::func(int)"<<endl; }
//派生類Derived
class Derived: public Base{
public:
    void func(char *);
    void func(bool);
};
void Derived::func(char *str){ cout<<"Derived::func(char *)"<<endl; }
void Derived::func(bool is){ cout<<"Derived::func(bool)"<<endl; }
int main(){
    Derived d;
    d.func("c.biancheng.net");
    d.func(true);
    d.func();  //error: no matching function for call to 'Derived::func()'|
    d.func(10);  //Derived::func(bool),這是因為實參“10”被隱式的轉化成了bool型別;
    d.Base::func();
    d.Base::func(100);
    return 0;
}

三、多型(函式覆蓋)

我們知道C++的虛擬函式是為了實現多型。對於虛擬函式,有以下的幾點需要注意:

1、 只有派生類的虛擬函式遮蔽基類的虛擬函式(函式原型相同)才能構成多型(通過基類指標訪問派生類函式)。

例如基類虛擬函式的原型為virtual void func1();,派生類虛擬函式的原型為virtual void func1(int i);,那麼當基類指標 p 指向派生類物件時,語句p -> func1(100);將會出錯,而語句p -> func1();將呼叫基類的函式。

這一點也比較好理解,基類指標p並不知道自己指向的是派生類的物件,才導致編譯p->func(100)時,指標p自然會認為自己指向的是基類的物件,而基類物件中並沒有這麼一個函式原型,所以報錯。例如:

#include<iostream>
using namespace std;
//基類Base
class Base{
public:
    virtual void func1();
};
void Base::func1(){ cout<<"Base::func1()"<<endl; }

class Derived: public Base{
public:
    virtual void func1(int);
    virtual void func2();
};
void Derived::func1(int i){cout << "Derived::func1(int i)" << endl; }
void Derived::func2(){ cout<< "Derived::func2()"<<endl; }

int main(){
    Base *b = new Derived;
    b->func1(); //多型
    //b->func1(100); //編譯通不過,因為編譯器只知道自己是個Base*,而Base中並沒有func1(int)這個函式;
    //b->func2();    //編譯通不過,因為編譯器只知道自己是個Base*,而Base中並沒有func2()這個函式;
    return 0;
}

2、構成多型時,仍然可以訪問基類的成員變數和成員函式,不過要加上類名和域解析符

如:

#include<iostream>
using namespace std;
//基類Base
class Base{
public:
    virtual void func();
};
void Base::func(){ cout<<"Base::func()"<<endl; }

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

int main(){
    Base *b = new Derived;
    b->func(); //Derived::func()
    b->Base::func(); //Base::func()
    return 0;
}

關於通過虛擬函式實現多型的的實現方式,是通過一個虛擬函式表來實現的,具體見:C++ 虛擬函式表解析。即,雖然通過多型能夠實現基類指標來呼叫派生類的函式,在派生類的物件模型中的虛擬函式表指標指向的虛擬函式表中也用派生類的func函式取代了基類的func函式(這是實現多型的根本原因),但是正如例子中看到的,通過指定類名,仍然可以訪問基類的成員變數和成員函式。