C++深度解析 C++對象模型分析(下) --- 繼承對象模型,多態對象模型,虛函數表,用C語言實現多態(50)

C++深度解析 C++對象模型分析(下) --- 繼承對象模型,多態對象模型,虛函數表,用C語言實現多態(50)

 

 

 

繼承對象模型

在C++編譯器的內部類可以理解爲結構體

子類是由 父類成員 疊加 子類新成員 得到的。

代碼如下:繼承對象模型初探

#include <iostream>
#include <string>

using namespace std;

class Demo
{
protected:
    int mi;
    int mj;
};

class Derived : public Demo
{
    int mk;
public:
    Derived(int i, int j, int k)
    {
        mi = i;
        mj = j;
        mk = k;
    }
    
    void print()
    {
        cout << "mi = " << mi << ", "
             << "mj = " << mj << ", "
             << "mk = " << mk << endl;
    }
};

struct Test
{
    int mi;
    int mj;
    int mk;
};

int main()
{
    cout << "sizeof(Demo) = " << sizeof(Demo) << endl;  // 8
    cout << "sizeof(Derived) = " << sizeof(Derived) << endl;  // 12
    
    Derived d(1, 2, 3);
    //把一個指針類型重新解釋爲另一個指針類型
    Test* p = reinterpret_cast<Test*>(&d);
    
    cout << "Before changing ..." << endl;
    d.print();
    
    //通過指針p改變d對象成員變量的值
    p->mi = 10;
    p->mj = 20;
    p->mk = 30;
    
    cout << "After changing ..." << endl;
    
    d.print();
    
    return 0;
}

結果如下:

分析:

成員函數存放在代碼段,實際對象只包含成員變量。

繼承對象模型,父類成員變量疊加子類新定義成員變量,父類成員變量排在前面,子類新定義成員變量排在後面。

 

 

 

多態對象模型

多態與虛函數的區別:

多態:面向對象理論中的一個概念,相同的行爲方式,不同的行爲結果,表現多種形態。

虛函數:多態的表現形式由虛函數實現。

C++多態的實現原理

  • 當類中聲明虛函數時,編譯器會在類中生成一個虛函數表
  • 虛函數表是一個存儲成員函數地址的數據結構
  • 虛函數是由編譯器自動生成與維護的
  • virtual成員函數會被編譯器放入虛函數表
  • 存在虛函數時,每個對象中都有一個指向虛函數表的指針
void run(Demo* p, int v)
{
    p->add(v);
}

編譯器確認run()是否爲虛函數:

如果run是虛函數,編譯器在對象VPTR所指向的虛函數表中查找add()的地址

如果run不是虛函數,編譯器直接可以確定被調用成員函數的地址

代表如下:

#include <iostream>
#include <string>

using namespace std;

class Demo
{
protected:
    int mi;
    int mj;
public:
    //裏面存在一個虛函數表指針
    virtual void print()
    {
        cout << "mi = " << mi << ", "
             << "mj = " << mj << endl;
    }
};

class Derived : public Demo
{
    int mk;
public:
    Derived(int i, int j, int k)
    {
        mi = i;
        mj = j;
        mk = k;
    }
    
    void print()
    {
        cout << "mi = " << mi << ", "
             << "mj = " << mj << ", "
             << "mk = " << mk << endl;
    }
};

struct Test
{
    void *p; //指向虛函數表的指針
    int mi;
    int mj;
    int mk;
};

int main()
{
    cout << "sizeof(Demo) = " << sizeof(Demo) << endl;
    cout << "sizeof(Derived) = " << sizeof(Derived) << endl;
    
    Derived d(1, 2, 3);
    //把一個指針類型重新解釋爲另一個指針類型
    Test* p = reinterpret_cast<Test*>(&d);
    
    cout << "Before changing ..." << endl;
    d.print();
    
    //通過指針p改變d對象成員變量的值
    p->mi = 10;
    p->mj = 20;
    p->mk = 30;
    
    cout << "After changing ..." << endl;
    
    d.print();
    
    return 0;
}

結果如下:

分析:

當創建對象時,如果類裏面有虛函數,最終生成對象時,會被編譯器強行塞入一個指針成員變量,這個指針成員變量是不可見的,但是它確實存在,這個指針成員變量指向虛函數表。

 

 

 

用C語言實現面向對象,用C語言實現多態

代碼如下:

50-2.h

#ifndef _50_2_H_

#define _50_2_H_



typedef void Demo;

typedef void Derived;//子類



Demo* Demo_Create(int i, int j);

int Demo_GetI(Demo* pThis);

int Demo_GetJ(Demo* pThis);

int Demo_Add(Demo* pThis, int value);

void Demo_Free(Demo* pThis);


//子類成員函數

Derived* Derived_Create(int i, int j, int k);

int Derived_GetK(Derived* pThis);

int Derived_Add(Derived* pThis, int value);//虛函數



#endif

50-2.c

#include "50-2.h"

#include "malloc.h"


//定義一個全局的虛函數,這個函數在當前文件能訪問
static int Demo_Virtual_Add(Demo* pThis, int value);
static int Derived_Virtual_Add(Demo* pThis, int value);

//用結構體來表示虛函數表
struct VTable      //2. 定義虛函數表數據結構
{
    //函數指針
    int (*pAdd)(Derived*, int);   //3. 虛函數表裏面存儲什麼???
};


struct ClassDemo

{
    struct VTable* vptr;//1. 定義虛函數表指針 ==》 虛函數表指針類型???

    int mi;

    int mj;

};



//父類的成員變量疊加子類的成員變量

struct ClassDerived

{

    //最開始的部分爲父類

    struct ClassDemo d;

    int mk;

};


//虛函數表變量,用送她題材
static struct VTable g_Demo_vtbl = 
{
    Demo_Virtual_Add
};

static struct VTable g_Derived_vtbl = 
{
    Derived_Virtual_Add
};


Demo* Demo_Create(int i, int j)

{

    struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));

    

    if( ret != NULL )

    {
        //指向虛函數表
        ret->vptr = &g_Demo_vtbl;   //4. 關聯對象和虛函數表

        ret->mi = i;

        ret->mj = j;

    }

    

    return ret;

}



int Demo_GetI(Demo* pThis)

{

     struct ClassDemo* obj = (struct ClassDemo*)pThis;

     

     return obj->mi;

}



int Demo_GetJ(Demo* pThis)

{

    struct ClassDemo* obj = (struct ClassDemo*)pThis;

     

    return obj->mj;

}


//6. 定義虛函數表中指針所指向的具體函數
static int Demo_Virtual_Add(Demo* pThis, int value)
{
    struct ClassDemo* obj = (struct ClassDemo*)pThis;

     

    return obj->mi + obj->mj + value;
}

//5. 分析具體的虛函數!!!!

int Demo_Add(Demo* pThis, int value)

{

    struct ClassDemo* obj = (struct ClassDemo*)pThis;

     
    //通過對象,找到虛函數表的指針,然後在虛函數表中找到具體調用函數的地址

    return obj->vptr->pAdd(pThis, value);

}



void Demo_Free(Demo* pThis)

{

    free(pThis);

}



//子類構造函數

Derived* Derived_Create(int i, int j, int k)

{

    struct ClassDerived* ret = (struct ClassDerived*)malloc(sizeof(struct ClassDerived));

    

    if( ret != NULL )

    {
        ret->d.vptr = &g_Derived_vtbl;
        //初始化父類

        ret->d.mi = i;

        ret->d.mj = j;
        //初始化子類
        ret->mk = k;

    }

    

    return ret;

}



int Derived_GetK(Derived* pThis)
{
    struct ClassDerived* obj = (struct ClassDerived*)pThis;

     

    return obj->mk;
}

static int Derived_Virtual_Add(Demo* pThis, int value)
{
    struct ClassDerived* obj = (struct ClassDerived*)pThis;

     

    return obj->mk + value;
}


int Derived_Add(Derived* pThis, int value)
{
    struct ClassDerived* obj = (struct ClassDerived*)pThis;

     

    return obj->d.vptr->pAdd(pThis, value);
}

main.c

#include "stdio.h"

#include "50-2.h"


void run(Demo* p, int v)
{
    int r = Demo_Add(p, 3);
    
    printf("r = %d\n", r);
}


int main()

{
    //創建父類對象
    Demo* pb = Demo_Create(1, 2);
    //創建子類對象
    Derived* pd = Derived_Create(1, 22, 333);
    
    printf("pb->add(3) = %d\n", Demo_Add(pb, 3));
    printf("pd->add(3) = %d\n", Derived_Add(pd, 3));
    
    run(pb, 3);
    run(pd, 3);
    
    Demo_Free(pb);
    Demo_Free(pd);
    

    return 0;

}

結果如下:

 

 

 

小結:

繼承的本質就是父子間成員變量的疊加

C++中的多態是通過虛函數表實現的

虛函數表是由編譯器自動生成與維護的

虛函數的調用效率低於普通成員函數

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章