1. 程式人生 > >c++單繼承與多繼承(包含虛擬函式與虛繼承的對比)

c++單繼承與多繼承(包含虛擬函式與虛繼承的對比)

先來個概念分析題:

class Person
{
public:
    void Show()
    {
        cout<<"Person::"<<_name<<endl;
    }
protected:
    int _id;
    string _name;
};

struct Student : public Person
{
public:
    void Show()
    {
        Person::_id = 10;
        cout<<"Student::"<<_name<<endl;
    }
public
: int _id; }; // 隱藏--重定義 class AA { public: void f() { cout<<"AA::f()"<<endl; } }; class BB : public AA { public: void f(int a) { cout<<"BB::f()"<<endl; } }; int main() { AA aa; BB bb; aa.f(); bb.f(); /*bb.AA::f();*/
system("pause"); return 0; // B C /*A. BB裡面有兩個f函式,並且構成過載 B. BB裡面有兩個f函式,並且構成隱藏 C.上面程式碼編譯器不過 D.上面程式碼可以編譯通過,輸出AA::f().*/ }

圖解:
這裡寫圖片描述
一、單繼承&多重繼承
1. 單繼承–一個子類只有一個直接父類時稱這個繼承關係為單繼承
2. 多繼承–一個子類有兩個或以上直接父類時稱這個繼承關係為多繼承

圖解:
這裡寫圖片描述
二、菱形繼承(會帶來資料冗餘和二義性)————-用虛繼承來解決(virtual)
這裡寫圖片描述
程式碼:

class A
{
public
: int _a; }; class B : virtual public A { public: int _b; }; class C : virtual public A { public: int _c; }; class D : public B, public C { public: int _d; }; int main() { D d; d.B::_a = 1; d.C::_a = 2; d._b = 3; d._c = 4; d._d = 5; D d1; cout<<sizeof(D)<<endl; B b; b._a = 7; b._b = 8; cout<<sizeof(B)<<endl; system("pause"); return 0; }

圖解:
這裡寫圖片描述
這裡寫圖片描述
解決了這個問題,但開銷較大,這兩個指標簡稱尾虛基表指標

三、虛擬函式:
虛擬函式&多型
虛擬函式–類的成員函式前面加virtual關鍵字,則這個成員函式稱為虛擬函式。 虛擬函式重寫–當在子類的定義了一個與父類完全相同的虛擬函式時,則稱子類的這個函式重寫(也稱覆蓋)了父類的這個虛擬函式。
總結:
1. 派生類重寫基類的虛擬函式實現多型,要求函式名、引數列表、返回值完全相同。(協變除外)
2. 基類中定義了虛擬函式,在派生類中該函式始終保持虛擬函式的特性
3. 只有類的成員函式才能定義為虛擬函式。
4. 靜態成員函式不能定義為虛擬函式。
5. 如果在類外定義虛擬函式,只能在宣告函式時加virtual,類外定義函式時不能加virtual。
6. 建構函式不能為虛擬函式,雖然可以將operator=定義為虛擬函式,但是最好不要將operator=定義為虛擬函式,因為容易使用時容易引 起混淆。
7. 不要在建構函式和解構函式裡面呼叫虛擬函式,在建構函式和解構函式中,物件是不完整的,可能會發生未定義的行為。
8. 最好把基類的解構函式宣告為虛擬函式。(why?另外解構函式比較特殊,因為派生類的解構函式跟基類的解構函式名稱不一樣,但 是構成覆蓋,這裡是因為編譯器做了特殊處理)
未完待續