C++多继承内存模型

1.说明

C++允许多继承(区别于java,不能多继承,需要使用接口),但是C++的多继承也带来了 用户层面理解盲区(例如:内存布局等)

2.代码测试

实现一个基础的继承

class A{
public:
    int a;
    int b;
    virtual void function1()
    {
        cout<<"this is class A ,function1 !"<<endl;
    }
};

class B{
public:
    int a;
    int b;
    virtual void function2()
    {
        cout<<"this is class B, function2 !"<<endl;
    }
};

//class C extend A B
class C :public A, public B //1. 没有指明这个bulic 默认就是private继承
{
public:
    int a;
    int b;
    virtual void function3()
    {
        cout<<"this is calss C, function3 !"<<endl;
    }

    //Class C里面新增加一个新的虚函数,这个函数和其他函数关系
    virtual void function4()
    {
        cout<<"this is new virtual, before C obj is 40 !"<<endl;//这个虚函数的地址,是放到了A的虚函数表,还是B的虚函数表??
    }
};
3.直接看结果

  • 多继承C类,实例化后含有AB类中的成员变量(public的,注意private的也是占大小的)
  • 注意访问继承的成员变量
  • static成员变量在全局区!!
4.补充

在实际使用中,我们重新划分了几种模型

4.1 无虚函数的多继承

很好理解,没有虚函数表,子类对象中,直接位置先按顺序排父类对象,注意字节对齐,就可以。

4.2 有虚函数的多继承

在原来无虚函数的多继承上,也是按顺序排父类对象,对于含有虚函数的父类,第一个位置, 先是虚函数表。

4.3 虚基类多继承(虚继承)

多增加需要理解虚继承。 为什么需要虚继承? 当一个类要继承多个父类,而这个父类们,又继承于一个base基类,也就是菱形继承,这样导致了二义性,因此,引入 方法,在继承是增加virtual,进行虚继承,来保证,派生类对象中包含一个指向虚基类的指针,这个指针指向 一个虚基类表,虚基类表中存储了偏移量信息,用于在运行时确定基类成员的具体位置,这样保证,派生通过 多个路径继承同一个基类,也能保证基类成员的唯一访问。 本质上,就是基类只有一份,在里面,前面的内存模型和有虚函数的多继承类似,base基类只有一份,在子类对象 后面。

//虚基类,多继承
class grandfather{
public:
    char *name;             //8字节
    virtual void grandfather_function(){

    }

    // 含有虚函数表8字节
};

class father_real : virtual public grandfather{
public:
    char* name;
    virtual void father1_function(){

    }
};

class uncle: virtual public grandfather{
public:
    char* name;
    virtual void father2_function(){

    }
};

class young_son : public father_real, public uncle{

public:
    char*   name;
};

其内存模型:

//----young son size
    vptr                  //8字节  |father_real  指向father_real的虚基类表
    char* name ;  //8字节   | father_real
    vptr                 //8字节   |uncle       指向uncle的虚基类表
    char* name ; //8字节    |uncle
    char* name ;//8字节     |young_son
    vptr               //8字节     |grandfather  指向grandfather的虚函数表
    char* name ;//8字节     |grandfather
5.引申注意

对于字节对齐问题,因为受编译器影响,可以指定( attribute((packed)))对齐大小。