1. 程式人生 > >C++繼承匯總(單繼承、多繼承、虛繼承、菱形繼承)

C++繼承匯總(單繼承、多繼承、虛繼承、菱形繼承)

虛基類表指針 www 地址 編譯 聲明 pre 繼承 第一個 src

一、C++中的對象模型

1、 概念

語言中直接支持面向對象程序設計的部分;

對於各種支持的底層實現機制。(沒看懂……)

2、 類中的成員分類

a) 成員函數

  i. static function

  ii. non static function

  iii. virtual function

b) 數據成員

  i. static member data

  ii. non static member data

3、 C++對象模型

a) 類對象內存布局中的包括

  i. non static member data

  ii. vptr(虛函數表指針)

  iii. vbptr(虛基類表指針)

b) 不包括

  i. static member data(存儲在靜態存儲區)

  ii. 成員函數(存儲在代碼區)

c) virtual table

簡稱vtbl。存放著指針,這些指針指向該類每一個虛函數。虛表中的函數地址將按聲明時的順序排列。vtbl在類聲明後就形成了,vptr是編譯器生成的。

d) vptr的位置一般放在一個類對象的最前端。

e) 虛基類表

vbptr指向的表,用於存放虛繼承中,虛基類存儲相對於虛基類表指針的偏移量。

技術分享

二、繼承類型

1、普通繼承(不包含虛函數)

a、單繼承

class Base
{
public:
    Base (int a = 1):base
(a){} void fun0(){cout << base << endl;} int base; }; class Derive:public Base { public: Derive (int a = 2):derive(a){} void fun1(){cout << base1 << endl;} int derive; };

技術分享

b、多繼承

class Base1
{
public:
    Base1 (int a = 2):base1(a){}
    void fun1(){cout << base1 << endl;}
    
int base1; }; class Base2 { public: Base2 (int a = 3):base2(a){} void fun2(){cout << base2 << endl;} int base2; }; class Derive: public Base1, public Base2 { public: Derive (int value = 4):derive (value){} void fun3(){cout << derive << endl;} int derive; };

技術分享

c、菱形繼承

class Base
{
public:
    Base (int a = 1):base(a){}
    void fun0(){cout << base << endl;}
    int base;
};
class Base1
{
public:
    Base1 (int a = 2):base1(a){}
    void fun1(){cout << base1 << endl;}
    int base1;
};
class Base2
{
public:
    Base2 (int a = 3):base2(a){}
    void fun2(){cout << base2 << endl;}
    int base2;
};
class Derive: public Base1, public Base2
{
public:
    Derive (int value = 4):derive (value){}
    void fun3(){cout << derive << endl;}
    int derive;
};

技術分享

註:菱形繼承存在二義性問題,編譯都不通過,只能通過指定特定基類的方式進行訪問基類變量。

Derive d;

d.base =3; // 不正確

d.Base1::base = 3; // 正確

2、普通繼承(包含虛函數)

  a、單繼承(包含虛函數)

class Base
{
public:
    Base (int a = 1):base(a){}
    virtual void fun0(){cout << base << endl;}
    int base;
};
class Derive:public Base
{
public:
    Derive (int a = 2):derive(a){}
    virtual void fun0(){};
    virtual void fun1(){cout << derive << endl;}
    int derive;
};

技術分享

註:派生類中新增的虛函數追加到虛函數表後面。

b、多繼承(包含虛函數)

class Base1
{
public:
    Base1 (int a = 2):base1(a){}
    virtual void fun1(){cout << base1 << endl;}
    int base1;
};
class Base2
{
public:
    Base2 (int a = 3):base2(a){}
    virtual void fun2(){cout << base2 << endl;}
    int base2;
};
class Derive: public Base1, public Base2
{
public:
    Derive (int value = 4):derive (value){}
    virtual void fun3(){cout << derive << endl;}
    int derive;
};

技術分享

註:派生類中新增的虛函數,追加到第一個基類的虛函數表的後面。

c、菱形繼承(包含虛函數)

class Base
{
public:
    Base (int a = 1):base(a){}
    virtual void fun0(){cout << base << endl;}
    int base;
};
class Base1:public Base
{
public:
    Base1 (int a = 2):base1(a){}
    virtual void fun1(){cout << base1 << endl;}
    int base1;
};
class Base2:public Base
{
public:
    Base2 (int a = 3):base2(a){}
    virtual void fun2(){cout << base2 << endl;}
    int base2;
};
class Derive: public Base1, public Base2
{
public:
    Derive (int value = 4):derive (value){}
    virtual void fun3(){cout << derive << endl;}
    int derive;
};

技術分享

註:分析時,由上到下依次分析。存在二義性問題和內存冗余問題。

3、虛繼承(不包含虛函數)

新增虛基類指針,指向虛基類表,虛基類表中首項存儲虛基類指針的偏移量,接下來依次存儲虛基類的偏移量(偏移量是相對於虛基類表指針的存儲地址)。

   a、單虛繼承(不包含虛函數)

class Base
{
public:
    Base (int a = 1):base(a){}
    void fun0(){cout << base << endl;}
    int base;
};
class Base1:virtual public Base
{
public:
    Base1 (int a = 2):base1(a){}
    void fun1(){cout << base1 << endl;}
    int base1;
};

技術分享

   b、多虛繼承(不包含虛函數)

class Base1
{
public:
    Base1 (int a = 2):base1(a){}
    void fun1(){cout << base1 << endl;}
    int base1;
};
class Base2
{
public:
    Base2 (int a = 3):base2(a){}
    void fun2(){cout << base2 << endl;}
    int base2;
};
class Derive:virtual public Base1, virtual public Base2
{
public:
    Derive (int value = 4):derive (value){}
    void fun3(){cout << derive << endl;}
    int derive;
};

技術分享

  c、菱形虛繼承(不包含虛函數)

   第一種形式:

class Base
{
public:
    Base (int a = 1):base(a){}
    void fun0(){cout << base << endl;}
    int base;
};
class Base1:virtual Base
{
public:
    Base1 (int a = 2):base1(a){}
    void fun1(){cout << base1 << endl;}
    int base1;
};
class Base2:virtual Base
{
public:
    Base2 (int a = 3):base2(a){}
    void fun2(){cout << base2 << endl;}
    int base2;
};
class Derive:virtual public Base1, virtual public Base2
{
public:
    Derive (int value = 4):derive (value){}
    void fun3(){cout << derive << endl;}
    int derive;
};

技術分享

註:分析派生類的內存分布時,也是由上到下分析。虛繼承將基類置於內存末尾,但是置於末尾的順序也有一定的次序。首先Base先放到末尾,然後Base1放到末尾,最後Base2放到末尾。

第二種形式:

class Base
{
public:
    Base (int a = 1):base(a){}
    void fun0(){cout << base << endl;}
    int base;
};
class Base1:virtual public Base
{
public:
    Base1 (int a = 2):base1(a){}
    void fun1(){cout << base1 << endl;}
    int base1;
};

class Base2:virtual public Base
{
public:
    Base2 (int a = 3):base2(a){}
    void fun2(){cout << base2 << endl;}
    int base2;
};
class Derive: public Base1, public Base2
{
public:
    Derive (int value = 4):derive (value){}
    void fun3(){cout << derive << endl;}
    int derive;
};

技術分享

註:分析的原則,從上到下,依次分析。

4、 虛繼承(包含虛函數)

a、單虛繼承(包含虛函數)

class Base
{
public:
    Base (int a = 1):base(a){}
    virtual void fun0(){cout << base << endl;}
    int base;
};
class Base1:virtual Base
{
public:
    Base1 (int a = 2):base1(a){}
    virtual void fun1(){cout << base1 << endl;}
    int base1;
};

技術分享

  與普通的包含虛函數的單繼承相比,派生類擁有自己的虛函數表以及虛函數表指針,而不是與基類共用一個虛函數表。註意虛函數表指針和虛基類表指針的存儲順序。

  b、多虛繼承(包含虛函數)

class Base1
{
public:
    Base1 (int a = 2):base1(a){}
    virtual void fun1(){cout << base1 << endl;}
    int base1;
};

class Base2
{
public:
    Base2 (int a = 3):base2(a){}
    virtual void fun2(){cout << base2 << endl;}
    int base2;
};
class Derive:virtual public Base1, virtual public Base2
{
public:
    Derive (int value = 4):derive (value){}
    virtual void fun3(){cout << derive << endl;}
    int derive;
};

技術分享

c、菱形虛繼承(包含虛函數)

第一種形式:

class Base
{
public:
    Base (int a = 1):base(a){}
    virtual void fun0(){cout << base << endl;}
    int base;
};
class Base1:virtual public Base
{
public:
    Base1 (int a = 2):base1(a){}
    virtual void fun1(){cout << base1 << endl;}
    int base1;
};

class Base2:virtual public Base
{
public:
    Base2 (int a = 3):base2(a){}
    virtual void fun2(){cout << base2 << endl;}
    int base2;
};
class Derive: virtual public Base1, virtual public Base2
{
public:
    Derive (int value = 4):derive (value){}
    virtual void fun3(){cout << derive << endl;}
    int derive;
};

技術分享

第二種形式:

class Base
{
public:
    Base (int a = 1):base(a){}
    virtual void fun0(){cout << base << endl;}
    int base;
};
class Base1:virtual public Base
{
public:
    Base1 (int a = 2):base1(a){}
    virtual void fun1(){cout << base1 << endl;}
    int base1;
};

class Base2:virtual public Base
{
public:
    Base2 (int a = 3):base2(a){}
    virtual void fun2(){cout << base2 << endl;}
    int base2;
};
class Derive: virtual public Base1,virtual public Base2
{
public:
    Derive (int value = 4):derive (value){}
    virtual void fun3(){cout << derive << endl;}
    int derive;
};

自行腦補C++類對象的內存結構……

註:上述虛函數中,如果派生類重寫了基類的虛函數,則對應虛函數表中的虛函數應該修改成重新後的虛函數,即Base::fun()->Derive::fun()。

參考鏈接:

  http://www.cnblogs.com/raichen/p/5744300.html

C++繼承匯總(單繼承、多繼承、虛繼承、菱形繼承)