浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第53课 被遗弃的多重继承(上)

Posted on 2016-06-24 23:08  浅墨浓香  阅读(342)  评论(1编辑  收藏  举报

1. 单一继承

(1)实验代码

#include <iostream>
#include <string>

using namespace std;

void visitVtbl(int **vtbl)
{
    cout << vtbl << endl;
    cout << "\t[-1]: " << (long)vtbl[-1] << endl;

    typedef void (*FuncPtr)();
    int i=0;
    while(vtbl[i]!=0)
    {
        cout << "\t[" << i << "]: " << vtbl[i] << " -> ";
        FuncPtr func = (FuncPtr)vtbl[i];
        func();
        ++i;
    }
}

class Base
{
    public:
        Base()
        {
            mBase1 = 101;
            mBase2 = 102;
        }
        virtual void func1()
        {
            cout << "Base::func1()" << endl;
        }
        virtual void func2()
        {
            cout << "Base::func2()" << endl;
        }
    private:
        int mBase1;
        int mBase2;
};

class Derived : public Base
{
    public:
        Derived():
            Base()
        {
            mDerived1 = 1001;
            mDerived2 = 1002;
        }
        virtual void func2()
        {
            cout << "Derived::func2()" << endl;
        }
        virtual void func3()
        {
            cout << "Derived::func3()" << endl;
        }
    private:
        int mDerived1;
        int mDerived2;
};

int main()
{
    Derived d;
    char *p = (char*)&d;
    visitVtbl((int**)*(int**)p);
    p += sizeof(int**);

    cout << *(int*)p << endl;
    p += sizeof(int);

    cout << *(int*)p << endl;
    p += sizeof(int);

    cout << *(int*)p << endl;
    p += sizeof(int);

    cout << *(int*)p << endl;

    return 0;
}

/*输出结果
0x404338
        [-1]: 4211452
        [0]: 0x402998 -> Base::func1()
        [1]: 0x402a20 -> Derived::func2()
        [2]: 0x402a50 -> Derived::func3()
101
102
1001
1002
*/
View Code

(2)对象的内存模型

 

(2)针对单一继承可以得出以下结论

  ①vptr位于对象的最前端。

  ②非static的成员变量根据其继承顺序和声明顺序排在vptr的后面。

  ③派生类继承基类所声明的虚函数,即基类的虚函数地址会被复制到派生类的虚函数表中的相应的项中。

  ④派生类中新加入的virtual函数跟在其继承而来的virtual的后面,如本例中,子类增加的virtual函数func3被添加到func2后面。

  ⑤若子类重写其父类的virtual函数,则子类的虚函数表中该virtual函数对应的项会更新为新函数的地址,如本例中,子类重写了virtual函数func2,则虚函数表中func2的项更新为子类重写的函数func2的地址。

2. 多重继承

(1)实验代码

#include <iostream>
#include <string>

using namespace std;

void visitVtbl(int **vtbl, int count)
{
    cout << vtbl << endl;
    cout << "\t[-1]: " << (long)vtbl[-1] << endl;

    typedef void (*FuncPtr)();
    for (int i = 0; vtbl[i] && i < count; ++i)
    {
        cout << "\t[" << i << "]: " << vtbl[i] << " -> ";
        FuncPtr func = (FuncPtr)vtbl[i];
        func();
    }
}

class Base1
{
    public:
        Base1()
        {
            mBase1 = 101;
        }
        virtual void funcA()
        {
            cout << "Base1::funcA()" << endl;
        }
        virtual void funcB()
        {
            cout << "Base1::funcB()" << endl;
        }
    private:
        int mBase1;
};

class Base2
{
    public:
        Base2()
        {
            mBase2 = 102;
        }
        virtual void funcA()
        {
            cout << "Base2::funcA()" << endl;
        }
        virtual void funcC()
        {
            cout << "Base2::funcC()" << endl;
        }
    private:
        int mBase2;
};

class Derived : public Base1, public Base2
{
    public:
        Derived():
            Base1(),
            Base2()
        {
            mDerived = 1001;
        }
        virtual void funcD()
        {
            cout << "Derived::funcD()" << endl;
        }
        virtual void funcA()
        {
            cout << "Derived::funcA()" << endl;
        }
    private:
        int mDerived;
};

int main()
{
    Derived d;
    char *p = (char*)&d;
    visitVtbl((int**)*(int**)p, 3);
    p += sizeof(int**);

    cout << *(int*)p << endl;
    p += sizeof(int);

    visitVtbl((int**)*(int**)p, 3);
    p += sizeof(int**);

    cout << *(int*)p << endl;
    p += sizeof(int);

    cout << *(int*)p << endl;

    return 0;
}

/*输出结果
0x4043a8
        [-1]: 4211520
        [0]: 0x402a88 -> Derived::funcA()
        [1]: 0x4029b8 -> Base1::funcB()
        [2]: 0x402ab8 -> Derived::funcD()
101
0x4043bc
        [-1]: 4211520
        [0]: 0x402b28 -> Derived::funcA()
        [1]: 0x402a38 -> Base2::funcC()
102
1001
*/
View Code

(2)对象的内存模型

 

(3)钍对多重继承可以得出以下结论

  ①在多重继承下,一个子类拥有n-1张额外的虚函数表,n表示其上一层的基类的个数。也就是说,在多重继承下,一个派生类会有n个虚函数表。其中一个为主要实例,它与第一个基类(如本例中的Base1)共享,其他的为次要实例,与其他基类(如本例中的Base2)有关。

  ②子类新声明的virtual函数,放在主要实例的虚函数表中。如本例中,子类新声明的与Base1共享的虚函数表中。

  ③每一个父类的子对象在子类的对象保持原样性,并依次按声明次序排列。

  ④若子类重写virtual函数,则其所有父类中的签名相同的virtual函数被会被改写。如本例中,子类重写了funcA函数,则两个虚函数表中的funcA函数的项均被更新为子类重写的函数的地址。这样做的目的是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

3. 重复继承:某个父类被间接地重复继承了多次

(1)实验代码

#include <iostream>
#include <string>

using namespace std;

void visitVtbl(int **vtbl, int count)
{
    cout << vtbl << endl;
    cout << "\t[-1]: " << (long)vtbl[-1] << endl;

    typedef void (*FuncPtr)();
    for (int i = 0; vtbl[i] && i < count; ++i)
    {
        cout << "\t[" << i << "]: " << vtbl[i] << " -> ";
        FuncPtr func = (FuncPtr)vtbl[i];
        func();
    }
}

class Base
{
    public:
        Base()
        {
            mBase = 11;
        }
        virtual void funcA()
        {
            cout << "Base::funcA()" << endl;
        }
        virtual void funcX()
        {
            cout << "Base::funcX()" << endl;
        }
    protected:
        int mBase;
};
class Base1 : public Base
{
    public:
        Base1():
            Base()
        {
            mBase1 = 101;
        }
        virtual void funcA()
        {
            cout << "Base1::funcA()" << endl;
        }
        virtual void funcB()
        {
            cout << "Base1::funcB()" << endl;
        }
    private:
        int mBase1;
};
class Base2 : public Base
{
    public:
        Base2():
            Base()
        {
            mBase2 = 102;
        }
        virtual void funcA()
        {
            cout << "Base2::funcA()" << endl;
        }
        virtual void funcC()
        {
            cout << "Base2::funcC()" << endl;
        }
    private:
        int mBase2;
};
class Derived : public Base1, public Base2
{
    public:
        Derived():
            Base1(),
            Base2()
        {
            mDerived = 1001;
        }
        virtual void funcD()
        {
            cout << "Derived::funcD()" << endl;
        }
        virtual void funcA()
        {
            cout << "Derived::funcA()" << endl;
        }
    private:
        int mDerived;
};

int main()
{
    Derived d;
    char *p = (char*)&d;
    visitVtbl((int**)*(int**)p, 4);
    p += sizeof(int**);

    cout << *(int*)p << endl;
    p += sizeof(int);

    cout << *(int*)p << endl;
    p += sizeof(int);

    visitVtbl((int**)*(int**)p, 3);
    p += sizeof(int**);

    cout << *(int*)p << endl;
    p += sizeof(int);

    cout << *(int*)p << endl;
    p += sizeof(int);

    cout << *(int*)p << endl;

    return 0;
}

/*输出结果
0x404408
        [-1]: 4211552
        [0]: 0x402b68 -> Derived::funcA()
        [1]: 0x402a08 -> Base::funcX()
        [2]: 0x402a88 -> Base1::funcB()
        [3]: 0x402b98 -> Derived::funcD()
11
101
0x404420
        [-1]: 4211552
        [0]: 0x402c08 -> Derived::funcA()
        [1]: 0x402a08 -> Base::funcX()
        [2]: 0x402b10 -> Base2::funcC()
11
102
1001
*/
View Code

(2)对象的内存模型

 

(3)钍对重复继承可以得出以下结论

  ①重复继承后,位于继承层次顶端的父类Base分别被子类Base1和Base2继承,并被类Derived继承。所以在D中有类的对象中,存在Base1的子对象,同时也存在Base2的子对象,这两个子对象都拥有Base子对象,所以Base子对象(成员mBase)在Derived中存在两份。

  ②二义性的原因。由于在子类的对象中,存在两份父类的成员,当在Derived类中使用如下语句:

  mBase = 1;

  就会产生歧义。因为在该对象中有两处的变量的名字都叫mBase,所以编译器不能判断究竟该使用哪一个成员变量。所以在访问Base中的成员时,需要加上域作用符来明确说明是哪一个子类的成员,如:

  Base1::mBase = 1;

  重复继承可能并不是我们想要的,C++提供虚拟继承来解决这个问题,下面详细讲解虚拟继承.

4. 单一虚拟继承:

(1)实验代码:具体代码如下(类的实现与重复继承中的代码相同,只是Base1的继承关系变为虚拟继承)

#include <iostream>
#include <string>

using namespace std;

typedef void (*FuncPtr)();

void visitVtbl(int **vtbl, int count)
{
    cout << vtbl << endl;
    cout << "\t[-1]: " << (long)vtbl[-1] << endl;

    for (int i = 0; vtbl[i] && i < count; ++i)
    {
        cout << "\t[" << i << "]: " << vtbl[i] << " -> ";
        FuncPtr func = (FuncPtr)vtbl[i];
        func();
    }
}

class Base
{
    public:
        Base()
        {
            mBase = 11;
        }
        virtual void funcA()
        {
            cout << "Base::funcA()" << endl;
        }
        virtual void funcX()
        {
            cout << "Base::funcX()" << endl;
        }
    protected:
        int mBase;
};
class Base1 : virtual public Base
{
    public:
        Base1():
            Base()
        {
            mBase1 = 101;
        }
        virtual void funcA()
        {
            cout << "Base1::funcA()" << endl;
        }
        virtual void funcB()
        {
            cout << "Base1::funcB()" << endl;
        }
    private:
        int mBase1;
};

int main()
{
    Base1 b1;
    char *p = (char*)&b1;
    visitVtbl((int**)*(int**)p, 2);
    p += sizeof(int**);

    cout << *(int*)p << endl;
    p += sizeof(int);

    visitVtbl((int**)*(int**)p, 2);
    p += sizeof(int**);

    cout << *(int*)p << endl;

    return 0;
}

/*输出结果
0x40434c
        [-1]: 4211448
        [0]: 0x4029d8 -> Base1::funcA()
        [1]: 0x402a08 -> Base1::funcB()
101
0x404364
        [-1]: 4211448
        [0]: 0x402a78 -> Base1::funcA()
        [1]: 0x402988 -> Base::funcX()
11
*/
View Code

(2)对象的内存模型

 

(3)钍对单一虚拟继承可以得出以下结论

  ①成员的顺序问题。在普通的单一继承中,基类的成员位于派生类的成员之前。而在单一虚继承中,首先是其普通基类的成员,接着是派生类的成员,最后是虚基类的成员。

  ②vptr的个数问题。在普通的单一继承中,派生类只有一个虚函数表,所以其对象只有一个vptr。而在单一虚继承中,派生类的虚函数表有n个(n为虚基类的个数)额外的虚数函数表,即总有n+1个虚函数表。

  ③派生自虚基类的派生类的虚函数表中,并不含有虚基类中的virtual函数,但是派生类重写的virtual函数会在所有虚函数表中得到更新。如本例中,第一个虚函数表中,并不含有Base::funcX的函数地址。

  ④一个类如果内含一个或多个虚基类子对象,像Base1那样,将会被分割为两部分:一个不变区域和一个共享区域。不变区域中的数据,不管后续如何变化,总是拥有固定的偏移量(从对象的开头算起),所以这一部分可以被直接存取。共享区域所对应的就是虚基类子对象。

5. 钻石型虚拟继承

(1)实验代码:具体代码如下(类的实现与重复继承中的代码相同,只是Base1和Base2的继承关系变为虚拟继承):

class Base  { ...... };

class Base1 : virtual public Base  { ...... };

class Base2 : virtual public Base  { ...... };

class Derived : public Base1, public Base2 { ...... };

#include <iostream>
#include <string>

using namespace std;

typedef void (*FuncPtr)();

void visitVtbl(int **vtbl, int count)
{
    cout << vtbl << endl;
    cout << "\t[-1]: " << (long)vtbl[-1] << endl;

    for (int i = 0; vtbl[i] && i < count; ++i)
    {
        cout << "\t[" << i << "]: " << vtbl[i] << " -> ";
        FuncPtr func = (FuncPtr)vtbl[i];
        //func();
    }
}

class Base
{
    public:
        Base()
        {
            mBase = 11;
        }
        virtual void funcA()
        {
            cout << "Base::funcA()" << endl;
        }
        virtual void funcX()
        {
            cout << "Base::funcX()" << endl;
        }
    protected:
        int mBase;
};
class Base1 : virtual public Base
{
    public:
        Base1():
            Base()
        {
            mBase1 = 101;
        }
        virtual void funcA()
        {
            cout << "Base1::funcA()" << endl;
        }
        virtual void funcB()
        {
            cout << "Base1::funcB()" << endl;
        }
    private:
        int mBase1;
};
class Base2 : virtual public Base
{
    public:
        Base2():
            Base()
        {
            mBase2 = 102;
        }
        virtual void funcA()
        {
            cout << "Base2::funcA()" << endl;
        }
        virtual void funcC()
        {
            cout << "Base2::funcC()" << endl;
        }
    private:
        int mBase2;
};
class Derived : public Base1, public Base2
{
    public:
        Derived():
            Base1(),
            Base2()
        {
            mDerived = 1001;
        }
        virtual void funcD()
        {
            cout << "Derived::funcD()" << endl;
        }
        virtual void funcA()
        {
            cout << "Derived::funcA()" << endl;
        }
    private:
        int mDerived;
};

int main()
{
    Derived d;
    char *p = (char*)&d;
    visitVtbl((int**)*(int**)p, 3);
    p += sizeof(int**);

    cout << *(int*)p << endl;
    p += sizeof(int);

    visitVtbl((int**)*(int**)p, 2);
    p += sizeof(int**);

    cout << *(int*)p << endl;
    p += sizeof(int);

    cout << *(int*)p << endl;
    p += sizeof(int);

    visitVtbl((int**)*(int**)p, 2);
    p += sizeof(int**);

    cout << *(int*)p << endl;

    return 0;
}

/*输出结果
0x40448c
        [-1]: 4211712
        [0]: 0x402b70 -> Derived::funcA()
        [1]: 0x402a68 -> Base1::funcB()
        [2]: 0x402ba0 -> Derived::funcD()
101
0x4044a4
        [-1]: 4211712
        [0]: 0x402c48 -> Derived::funcA()
        [1]: 0x402b04 -> Base2::funcC()
102
1001
0x4044bc
        [-1]: 4211712
        [0]: 0x402c68 -> Derived::funcA()
        [1]: 0x4029e8 -> Base::funcX()
11
*/
View Code

(2)对象的内存模型

 

(3)钍对钻石型虚拟继承可以得出以下结论

  ①使用虚继承后,在派生类的对象中只存在一份的Base子对象,从而避免了二义性。由于是多重继承,且有一个虚基类(Base),所以Derived类拥有三个虚函数表,其对象存在三个vptr。如上图所示,第一个虚函数表是由于多重继承而与第一基类(Base1)共享的主要实例,第二个虚函数表是与其他基类(Base2)有关的次要实例,第三个是虚基类的虚函数表。

  ②类Derived的成员与Base1中的成员排列顺序相同,首先是以声明顺序排列其普通基类的成员,接着是派生类的成员,最后是虚基类的成员。

  ③派生自虚基类的派生类的虚函数表中,也不含有虚基类中的virtual函数,派生类重写的virtual函数会在所有虚函数表中得到更新。

  ④在类Derived的对象中,Base(虚基类)子对象部分为共享区域,而其他部分为不变区域。