首页 > 代码库 > 第五十一课、c++对象模型分析(下)

第五十一课、c++对象模型分析(下)

一、继承的本质

1、在c++编译器的内部类可以理解为结构体

2、子类是由父类成员叠加子类新成员得到的

技术分享

 

 

二、多态的本质

1、当类中声明一个虚函数时,编译器会在类中生成一个虚函数表

2、虚函数表是一个存储成员函数地址的数据结构

3、虚函数表是由编译器自动生成与维护

4、virtual成员函数会被编译器放入虚函数表中

5、存虚函数时,每个对象都有一个指向虚函数表的指针

技术分享

技术分享

技术分享

#include<iostream>

using namespace std;

class Demo
{
protected:    
    int mi;
    int mj;
public:
    virtual void print()
    {
        cout << "mi = " << mi <<","
             << "mj = " << mj << endl;
    }
};

class Derived : public Demo
{
private:
    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;//12而不是8,原因:在类里边塞入了一个指向虚函数表的指针
    cout << "sizeof(Derived) = " << sizeof(Derived) << endl;//16而不是12

    Derived d(1, 2, 3);
    struct Test *p = reinterpret_cast<Test*>(&d);//说明结构体和对象的内存分布相同,第一个为指向虚函数表的指针,后面依次是mi、mj、mk

    cout << "Before Chinging..." << endl;
    d.print();//mi = 1, mj = 2, mk = 3

    p->mi = 10;
    p->mj = 20;
    p->mk = 30;
    
    cout << "After Chinging..." << endl;
    d.print();//mi = 10, mj = 20, mk = 30
    return 0;
}

三、用C语言实现面向对象

1、必须实现三大特性:封装、继承、多态

2、用void*指针保证结构体成员不被外界访问以实现封装

3、通过父类结构体作为子类结构体的第一个成员实现继承

4、用结构体来模拟虚函数表以实现多态

(1)、在类中定义虚函数表的指针

(2)、根据指针类型定义相应类型的虚函数表结构并填充相应的虚函数指针

(3)、生成子类和父类的虚函数表,由c++知虚函数表由编译器使用,故模拟时加上static关键字

(4)、关联具体对象虚函数表

(5)、实现真正的虚函数,也是static

(6)、改造虚函数接口(在虚函数表中找到具体的实现函数)

(7)、用类似方法完善子类

//add.h:

#ifndef _ADD_H_
#define _ADD_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);//重写add函数

#endif

//add.c:

#include"add.h"
#include<malloc.h>

static int Demo_virtual_add(Demo* pThis, int value);
static int Derived_virtual_add(Demo* pThis, int value);

struct VTable
{
    int (*pAdd)(void*, int);//2.定义虚函数表结构体并且填充相应的虚函数指针
};

struct ClassDemo
{
    struct VTable *ptr;//1.在类中定义指向虚函数表的指针
    int mi;
    int mj;
};

struct ClassDerived
{
    struct ClassDemo d;
    int mk;
};

static struct VTable Demo_virtual_table = {Demo_virtual_add};//3.生成子类和父类的虚函数表,由c++知虚函数表由编译器使用,故模拟时加上static关键字
static struct VTable Derived_virtual_table = {Derived_virtual_add};//3

//Demo父类
Demo* Demo_create(int i, int j)
{
    struct ClassDemo* ret = malloc(sizeof(struct ClassDemo));
    
    if(ret != NULL)
    {
        ret->ptr = &Demo_virtual_table;//4.关联具体对象与虚函数表
        ret->mi = i;
        ret->mj = j;
    }

    return ret;
}

int Demo_GetI(Demo* pThis)
{
    struct ClassDemo* ret = (struct ClassDemo *)(pThis);
    
    return ret->mi;
}

int Demo_GetJ(Demo* pThis)
{
    struct ClassDemo* ret = (struct ClassDemo *)(pThis);

    return ret->mj;
}

static int Demo_virtual_add(Demo* pThis, int value)//5.实现真正的虚函数
{
    struct ClassDemo* ret = (struct ClassDemo *)(pThis);

    return (ret->mi + ret->mj + value);
}

int Demo_add(Demo* pThis, int value)
{
    struct ClassDemo* ret = (struct ClassDemo *)(pThis);

    return ret->ptr->pAdd(pThis, value);//6.改造虚函数的接口
}

void Demo_free(Demo* pThis)
{
    free(pThis);
}

//Derived子类
Derived* Derived_create(int i, int j, int k)
{
    struct ClassDerived* ret = malloc(sizeof(struct ClassDerived)); 
    if(ret != NULL)
    {
        ret->d.ptr = &Derived_virtual_table;
        ret->d.mi = i;
        ret->d.mj = j;
        ret->mk = k;
    }
    return ret;
}

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

    return ret->mk;
}

static int Derived_virtual_add(Demo* pThis, int value)
{
    struct ClassDerived* ret = (struct ClassDerived*)(pThis);

    return ret->mk + value;
}

int Derived_add(Derived* pThis, int value)
{
    struct ClassDerived* ret = (struct ClassDerived*)(pThis);

    return ret->d.ptr->pAdd(pThis, value);
}

//main.c:

#include<stdio.h>
#include<malloc.h>
#include"add.h"

void run(Demo* p, int v)
{
    int r = Demo_add(p, v);//模仿c++,默认调用父类的成员函数
    printf("run(p,v) = %d\n", r);
}

int main()
{
    Demo* pb = Demo_create(1, 2);
    Derived* pd = Derived_create(4, 5, 6);

    
    printf("Demo_add(pb,3) = %d\n", Demo_add(pb, 3));//1+2+3=6
    printf("Derived_add(pd,3) = %d\n", Derived_add(pd, 3));//6+3=9

    run(pb, 3);//1+2+3=6
run(pd, 3);//6+3=9 ===>Demo_add(pd,3)===>
return pd->ptr->pAdd(pd, 3)====>pd->ptr = &Demo_virtual_table
    Demo_free(pb);
Demo_free(pd);//继承父类的析构函数

return 0;
}

 

四、小结

1、继承的本质就是父子间成员变量的叠加

2、c++的多态是通过虚函数表来实现的

3、虚函数表是由编译器自动生成和维护

4、虚函数的调用效率低于普通成员函数

 

第五十一课、c++对象模型分析(下)