继承体系下C++对象模型

一直对 C++ 继承体系中对象的内存分布和实现原理不甚了解,故花了点时间复习整理了一下,如下:

C++ 的继承关系可分为如下几种情况:

下面分别来讨论:

单一的一般继承

这个没什么好说的,基类中有虚函数,所以编译器为其产生了虚表指针,该指针指向一个虚函数表,该虚函数表以 NULL 结尾(windows 下)。子类继承父类,如果符合虚函数覆盖的条件,就会覆盖父类的虚函数,由此产生了多态。如果子类新增了一些虚函数,就会添加在子类对象中父类部分的虚函数表的尾部。

代码和对应的内存分布图如下:

//case 1:单一的一般继承(带成员变量,虚函数,虚函数覆盖)
class parent
{
public:
    virtual void f(void)
    {
        cout << "parent::f()" << endl;
    }
    virtual void g()
    {
        cout << "parent::g()" << endl;
    }
    virtual void h()
    {
        cout << "parent::h()" << endl;
    }
    parent() :pi(10)
    {}
private:
    int pi;
};

class child :public parent
{
public:
    virtual void f()
    {
        cout << "child::f()" << endl;
    }
    virtual void g_child()
    {
        cout << "child::g_child()" << endl;
    }
    virtual void h_child()
    {
        cout << "child::h_child()" << endl;
    }
    child() :ci(100)
    {}
private:
    int ci;
};

class  grandchild :public child
{
public:
    virtual void f()
    {
        cout << "grandchild::f()" << endl;
    }
    virtual void g_child()
    {
        cout << "grandchild::g_child()" << endl;
    }
    virtual void h_grandchild()
    {
        cout << "grandchild::h_grandchild()" << endl;
    }
    grandchild() :gci(1000)
    {}
private:
    int gci;
};
void testcase1()
{
    grandchild gc;
    int **p = (int**)&gc;
    int i = 0;
    typedef void(*fun)(void);
    while (*(*p + i) != NULL)
    {
        ((fun)*((*p) + i++))();
    }
}

多重继承

多重继承的实现和单一继承的原理差不多,只不过在多重继承里,子类会覆盖所有父类对应的虚函数,并且子类新增的虚函数只添加到第一个被继承的父类的虚函数表尾部,这样主要是为了效率问题。

代码和内存图如下:

//case 2:多重继承(带成员变量,虚函数,虚函数覆盖)
class base1
{
public:
    virtual void f()
    {
        cout << "base1::f()" << endl;
    }
    virtual void g()
    {
        cout << "base1::g()" << endl;
    }
    virtual void h()
    {
        cout << "base1::h()" << endl;
    }
    base1() :ba1(10)
    {}
private:
    int ba1;

};

class base2
{
public:
    virtual void f()
    {
        cout << "base2::f()" << endl;
    }
    virtual void g()
    {
        cout << "base2::g()" << endl;
    }
    virtual void h()
    {
        cout << "base2::h()" << endl;
    }
    base2() :ba2(100)
    {}
private:
    int ba2;

};
class base3
{
public:
    virtual void f()
    {
        cout << "base3::f()" << endl;
    }
    virtual void g()
    {
        cout << "base3::g()" << endl;
    }
    virtual void h()
    {
        cout << "base3::h()" << endl;
    }
    base3() :ba3(1000)
    {}
private:
    int ba3;

};

class derived : public base1, public base2, public base3
{
public:
    virtual void f()
    {
        cout << "derived::f()" << endl;
    }
    virtual void g_derived()
    {
        cout << "derived::g_derived()" << endl;
    }
    derived() :di(10000)
    {}
private:
    int di;
};

void testcase2()
{
    derived d;
    int** p = (int**)&d;
    int i = 0;
    typedef void(*fun)(void);
    while (*(*p + i) != NULL)
    {
        ((fun)*((*p) + i++))();
    }
    cout << endl;
    i = 0;
    while (*(*p + 1 + i) != NULL)
    {
        ((fun)*((*p) + i++))();
    }
    cout << endl;
    i = 0;
    while (*(*p + 2 + i) != NULL)
    {
        ((fun)*((*p) + i++))();
    }
}

菱形继承

有时候在逻辑上需要菱形继承。所谓菱形继承,也就是两个或多个派生自同一个父类的类又派生出一个共同的子类。如下:

class base{};
class A:public base{};
class B:public base{};
class derived:public A,public B{}

但菱形继承有一个问题就是 base 类的部分在一个 derived 对象中会存在两份,这样不仅浪费空间,还会导致数据访问的二义性和不一致问题。二义性可以在前面加上对应类的作用域解决,数据不一致问题可以用下面将要讲的虚拟继承解决。

代码和内存图如下:

//case 3:菱形继承(带成员变量,虚函数,虚函数覆盖)
class base
{
public:
    virtual void f()
    {
        cout << "base::f()" << endl;
    }
    virtual void g()
    {
        cout << "base::g()" << endl;
    }
    virtual void h()
    {
        cout << "base::h()" << endl;
    }
    base() :bi(10)
    {}
private:
    int bi;
};

class base1 :public base
{
public:
    virtual void f()
    {
        cout << "base1::f()" << endl;
    }
    virtual void g()
    {
        cout << "base1::g()" << endl;
    }
    virtual void h_base1()
    {
        cout << "base1::h_base1()" << endl;
    }
    base1() :b1i(100)
    {}
private:
    int b1i;
};
class base2 :public base
{
public:
    virtual void f()
    {
        cout << "base2::f()" << endl;
    }
    virtual void g()
    {
        cout << "base2::g()" << endl;
    }
    virtual void h_base2()
    {
        cout << "base2::h_base2()" << endl;
    }
    base2() :b2i(1000)
    {}
private:
    int b2i;
};

class derived :public base1, public base2
{
public:
    virtual void f()
    {
        cout << "derived::f()" << endl;
    }
    virtual void g()
    {
        cout << "derived::g()" << endl;
    }
    virtual void h_derived()
    {
        cout << "derived::h_derived()" << endl;
    }
    derived() :di(10000)
    {}
private:
    int di;
};

void testcase3()
{
    derived d;
    int **p = (int**)&d;
    int i = 0;
    typedef void(*fun)(void);
    while (*(*p + i) != NULL)
    {
        ((fun)*((*p) + i++))();
    }
    cout << endl;
    i = 0;
    while (*(*p + 1 + i) != NULL)
    {
        ((fun)*((*p) + i++))();
    }
}

菱形虚拟继承

为了解决菱形继承中数据的不一致问题,可以使用虚拟继承来解决。也就是说虚拟继承是专为菱形继承而生的,在单一继承中使用虚拟继承不会有什么作用,反而会增加运行的开销,拖慢程序速度。这也就是我为什么没有列出单一虚拟继承的原因。

以如下代码为例子:

class base{};
class base1{}:public virtual base{};
class base2{}:public virtual base{};
class derived:public base1,public base2{};

在 derived 对象中,只存在一个 base 部分的实例,原因如下:

base 部分由 derived 构造且 base1 和 base2 对象中各有一个指向刚刚构造出来的 base 对象的指针。因此可以保证 derived 对象中只存在一个 base 的实例。

在虚继承中,中间层派生类的虚函数不会追加到父类的虚函数表后面,而是有自己单独的虚表指针。最底层派生类新增的虚函数会增加到第一个被继承的类的虚函数表的尾部(base 类在 base1 和 base2 的后面)。

代码和内存图如下:

//case 4:菱形虚拟继承(带成员变量,虚函数,虚函数覆盖)
class base
{
public:
    virtual void f()
    {
        cout << "base::f()" << endl;
    }
    virtual void g()
    {
        cout << "base::g()" << endl;
    }
    virtual void h()
    {
        cout << "base::h()" << endl;
    }
    base(int x) :bi(x)
    {}
private:
    int bi;
};

class base1 :virtual public base
{
public:
    virtual void f()
    {
        cout << "base1::f()" << endl;
    }
    virtual void g()
    {
        cout << "base1::g()" << endl;
    }
    virtual void h_base1()
    {
        cout << "base1::h_base1()" << endl;
    }
    base1() :b1i(100),base(100)
    {}
private:
    int b1i;
};
class base2 :virtual public base
{
public:
    virtual void f()
    {
        cout << "base2::f()" << endl;
    }
    virtual void g()
    {
        cout << "base2::g()" << endl;
    }
    virtual void h_base2()
    {
        cout << "base2::h_base2()" << endl;
    }
    base2() :b2i(1000),base(1000)
    {}
private:
    int b2i;
};

class derived :public base1, public base2
{
public:
    virtual void f()
    {
        cout << "derived::f()" << endl;
    }
    virtual void g()
    {
        cout << "derived::g()" << endl;
    }
    virtual void h_derived()
    {
        cout << "derived::h_derived()" << endl;
    }
    derived() :di(10000),base(10000)
    {}
private:
    int di;
};

void testcase4()
{
    base b(1);
    base1 b1;
    base2 b2;
    derived d;
    int **p = (int**)&d;
    int i = 0;
    typedef void(*fun)(void);
    while (*(*p + i) != NULL)
    {
        ((fun)*((*p) + i++))();
    }
    cout << endl;
    i = 0;
    while (*(*p + 1 + i) != NULL)
    {
        ((fun)*((*p) + i++))();
    }
}