天天看点

C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型

以下测试均在VS2013中进行。

1.带虚函数的单继承内存布局

看下面一段例子

#include <iostream>
using namespace std;

class A
{
public:
    virtual void test1()
    {
        cout << "A::test1" << endl;
    }
    virtual void test3()
    {
        cout << "A::test3" << endl;
    }
    int _a;
};

class B:public A
{
public:
    virtual void test1()
    {
        cout << "B::test1" << endl;
    }
    virtual void test2()
    {
        cout << "B::test2" << endl;
    }
    int _b;
};

typedef void(*PF)();
void print(B *p)
{
    PF* tmp = (PF*)*(int*)p;
    while (*tmp)
    {
        (*tmp)();
        tmp++;
    }
}

int main()
{
    B t;
    cout << sizeof(t) << endl;
    t._a = ;
    t._b = ;
    print(&t);
    return ;
}
           
C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型

首先用sizeof求出对象的大小为12个字节,其中前4个字节为虚函数指针,它指向一个虚函数表(也就是一个函数指针数组),其中存放的是虚函数的地址,最后一项为零。

那么虚函数表中是哪几个函数呢?

既然虚函数表中是函数的地址,那么只要我们拿到函数的地址就可以进行调用,根据输出结果即可判定是哪个函数。我们借助了一个辅助函数print,具体实现间源代码。

print函数的形参是用来接收对象的地址的。

*(int*)p: 将p转换为整形地址,并取出对象的前4个字节中的内容,即虚表指针,但此 时值为整形

(PF*)*(int*)p:由于数组中值的类型为PF,在将上一步的续表指针强转为PF*(可类比int数组,数组中值的类型为int,则指针类型为int*)

通过调用print函数显示结果如下图

C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型

可以看出,派生类先把基类的虚函数表继承下来,如果重写了基类的某个虚函数,则将虚函数表中这个函数该为派生类的(本例子为test1函数),再把派生类自己的虚函数加入到虚函数表中。

2.带虚函数的多继承内存布局、

class A
{
public:
    virtual void test1()
    {
        cout << "A::test1" << endl;
    }
    virtual void test3()
    {
        cout << "A::test3" << endl;
    }
    int _a;
};

class B
{
public:
    virtual void test1()
    {
        cout << "B::test1" << endl;
    }
    virtual void test2()
    {
        cout << "B::test2" << endl;
    }
    int _b;
};

class C :public A, public B
{
public:
    virtual void test1()
    {
        cout << "C::test1" << endl;
    }
    virtual void test3()
    {
        cout << "C::test3" << endl;
    }
    virtual void test4()
    {
        cout << "C::test4" << endl;
    }
    int _c;

};
           
C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型
C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型

可以看出,在本例中多继承时有两个虚函数指针,并且继承时先声明的类位于内存中最前面,当派生类中有自己的虚函数时它放在第一张虚表中。

3.带虚函数的菱形继承内存布局

C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型
C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型

从图中可以看出,菱形继承就是单继承与多继承的结合。

4.虚拟继承(不带虚函数)

#include <iostream>
using namespace std;
class A
{
public:
    void fun()
    {
        cout << "A::fun" << endl;
    }
    int _a;
};
class B :virtual public A
{
public:
    int _b;
};

int main()
{
    B t;
    cout << sizeof(t) << endl;
    t._a = ;
    t._b = ;
    return ;
}
           
C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型

可以看到,对象前四个字节依旧是一个指针,它指向的是一个偏移量表格,表格中的第一项是派生类自身部分相对于指针的偏移量,第二项是派生类对象中基类部分相对于指针的偏移量,刚好是8个字节。

此时,基类部分位于后半部分,而派生类部分位于前半部分。

5.菱形虚拟继承

C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型
#include <iostream>
#include <string>
using namespace std;

class A
{
public:
    virtual void test1()   //B1、B2、C均类重写
    {
        cout << "A::test1" << endl;
    }
    virtual void test3()  //A类独有的函数
    {
        cout << "A::test3" << endl;
    }
    int _a;
};

class B1: virtual public A
{
public:
    virtual void test1()
    {
        cout << "B1::test1" << endl;
    }
    virtual void test2() //B1独有的函数
    {
        cout << "B1::test2" << endl;
    }
    int _b1;
};
class B2 : virtual public A
{
public:
    virtual void test1()
    {
        cout << "B2::test1" << endl;
    }
    virtual void test4()  //C重写的函数
    {
        cout << "B2::test4" << endl;
    }
    int _b2;
};

class C :public B1, public B2
{
public:
    virtual void test1()
    {
        cout << "C::test1" << endl;
    }
    virtual void test5()    //C独有的函数
    {
        cout << "C::test5" << endl;
    }
    virtual void test4()
    {
        cout << "C::test4" << endl;
    }
    int _c;

};

typedef void(*PF)();
void print(A *p, const string& str)
{
    cout << str << endl;
    PF* tmp = (PF*)*(int*)p;
    while (*tmp)
    {
        (*tmp)();
        tmp++;
    }
    cout << endl;
}

void print(B1 *p, const string& str)
{
    cout << str << endl;
    PF* tmp = (PF*)*(int*)p;
    while (*tmp)
    {
        (*tmp)();
        tmp++;
    }
    cout << endl;
}

void print(B2 *p, const string& str)
{
    cout << str << endl;
    PF* tmp = (PF*)*(int*)p;
    while (*tmp)
    {
        (*tmp)();
        tmp++;
    }
    cout << endl;
}

int main()
{
    C t;
    cout << sizeof(t) << endl;
    t._a = ;
    t._b1 = ;
    t._b2 = ;
    t._c = ;
    B1 *b1 = &t;
    B2 *b2 = &t;
    A *a = b1;

    print(a, "A");
    print(b1, "B1");
    print(b2, "B2");
    cout << endl;
    return ;
}
           
C++中单继承、多继承、菱形继承、虚拟菱形继承的对象模型

继续阅读