美文网首页
多重继承带来的问题

多重继承带来的问题

作者: 九楼记 | 来源:发表于2022-03-23 23:17 被阅读0次

《More effective C++》 Item24

基础概念

虚函数的实现
两个关键词:virtual table和virtual table pointers,通常称为vtbl和vptr。
vtbl通常是一个函数指针数组。

Base *p = new Derive();
p->Print();

(*p->vptr[i])(p);

虚函数不能是内联函数。
因为内联函数在编译期间用被调用的函数体本身来代替函数调用的指令。

多重继承对虚函数带来的问题

虚函数表是属于类的,所以在多重继承里,在单个对象里就会有多个vptr(每个基类对应一个),除了自己的vtbl之外,还需要为基类生成特殊的vtbl,因此增加了每个类和每个对象中的虚函数额外占用的空间,而且运行时调用所需的代价也增 加了一些。

多重继承带来的虚继承问题

多继承经常导致对虚基类的需求。没有虚基类,如果一个派生类有一个以上从基类的继承路径,基类的数据成员被复制到每一个继承类对象里,继承类与基类间的每条路径都有一个拷贝。

菱形继承的缺点:
数据冗余:在Derive中会保存两份Base基类的内容
访问不明确(二义性):因为Derive不知道是以B为中介去访问A还是以C为中介去访问A,因此在访问某些成员的时候会发生二义性。

class A
{
public:
    A(int a) :m_a(a) {}
    int getMa() { return m_a; }
private:
    int m_a;
};

class B :public A
{
public:
    B(int a, int b) :A(a), m_b(b) {}
private:
    int m_b;
};

class C :public A
{
public:
    C(int a, int c) :A(a), m_c(c) {}
private:
    int m_c;
};

class D :public B, public C
{
public:
    D(int a, int b, int c, int d) :B(a, b), C(a, c), m_d(d) {}
    void func()
    {
        /*错误,访问不明确
        std::cout << getMa();*/

        //正确,通过B访问getMa()
        std::cout << B::getMa();
    }
private:
    int m_d;
};

虚继承

虚继承的作用:为了保证公共继承对象在创建时只保存一分实例
虚继承解决了菱形继承的两个问题:
数据冗余:顶级基类在整个体系中只保存了一份实例
访问不明确(二义性):可以不通过作用域访问符::来调用(原理就是因为顶级基类在整个体系中只保存了一份实例)

class CBase { };
class CDerive1:virtual public CBase{ };
class CDerive2:virtual public CBase{ };
class CDerive12:public CDerive1,CDerive2{ };

虚继承的实现原理

虚继承底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。

vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。

在这里我们可以对比虚函数的实现原理:他们有相似之处,都利用了虚指针(均占用类的存储空间)和虚表(均不占用类的存储空间)。

虚基类依旧存在继承类中,只占用存储空间;虚函数不占用存储空间。

虚基类表存储的是虚基类相对直接继承类的偏移;而虚函数表存储的是虚函数地址。

#include<iostream>
using namespace std;
 
class A  //大小为4
{
public:
    int a;
};
class B :virtual public A  //大小为12,变量a,b共8字节,虚基类表指针4
{
public:
    int b;
};
class C :virtual public A //与B一样12
{
public:
    int c;
};
class D :public B, public C //24,变量a,b,c,d共16,B的虚基类指针4,C的虚基类指针
{
public:
    int d;
};
 
int main()
{
    A a;
    B b;
    C c;
    D d;
    cout << sizeof(a) << endl;
    cout << sizeof(b) << endl;
    cout << sizeof(c) << endl;
    cout << sizeof(d) << endl;
    system("pause");
    return 0;
}

reference

[1] https://blog.51cto.com/u_15346415/3674072
[2] https://www.yangshuaibin.com/detail/376699
[3] https://blog.csdn.net/bxw1992/article/details/77726390

相关文章

  • 多重继承带来的问题

    《More effective C++》 Item24 基础概念 虚函数的实现两个关键词:virtual tabl...

  • 多重继承问题

    java中一个类不允许继承多个类,但一个接口允许继承多个接口 类与类之间用继承extends,一个类只允许继承一个...

  • Java中关于多重继承的问题

    多重继承的含义 一.用接口实现多重继承 二.用内部类实现多重继承

  • 慕课网-C++远征之继承篇(下)-学习笔记

    C++远征之继承篇(下) 多继承与多重继承 多重继承: 多继承: 如果不写,那么系统默认为private继承 多重...

  • 类的继承

    继承基本概念 继承关系可以解决代码的重复问题。 单继承;不能多重继承,允许多层继承。 继承类图表示:实线箭头,箭头...

  • java为什么不支持多重继承

    java之父说多重继承是很少用且被经常误解而错用的功能。 有著名的多重继承钻石问题,两个父类中存在相同方法,该继承...

  • 面向对象

    类的定义方法 子类的定义 多重继承 python允许多重继承。

  • 4.0 C++远征:多继承和多重继承

    5-1多继承和多重继承 1.多重继承 线性继承两次及其以上为多重继承。 2.多继承 一个派生类同时有多个基类。

  • Python中super的用法实例

    super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到...

  • python3 多重继承机制

    由于python中包含多重继承机制,那么子类在多重继承中,到底用的是哪一个超类的方法就是大家关心的问题,之前在查阅...

网友评论

      本文标题:多重继承带来的问题

      本文链接:https://www.haomeiwen.com/subject/xbvhjrtx.html