精华内容
下载资源
问答
  • 1.为什么要引入虚拟继承虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、...
  • 虚拟继承

    千次阅读 2018-07-03 17:10:27
    上一篇我们已经讨论了继承体系下派生类的对象模型https://blog.csdn.net/a15929748502/article/details/80893870 这一篇我们来解决上一篇中提出的问题:在菱形继承里,B的成员变量在D中存了两份,这...虚拟继承的...

    上一篇我们已经讨论了继承体系下派生类的对象模型https://blog.csdn.net/a15929748502/article/details/80893870

    这一篇我们来解决上一篇中提出的问题:在菱形继承里,B的成员变量在D中存了两份,这显然是不合理的,不仅浪费了空间,还产生了二义性,如何避免这种事情的发生呢,这就是接下来要讨论的问题

    这里就不得不提到今天的主题虚拟继承

    虚拟继承的作用;针对上片的问题具体说虚拟继承可以在上述情况中起到节省空间的作用,同时也解决了二义性问题,它会让B成为一个虚拟基类被C1和C2继承,这时D再继承C1,C2,这时D就只会保存一份B中的成员了。

    也就是说他可以解决从不同途径继承来的同一基类,会在子类中存在多份拷贝,浪费空间,并且产生二义性的问题

    虚拟继承的格式:

    class   子类名称:virtual   继承权限    父类名称

     {

               。。。。。。

    };   

    它是通过什么方式解决呢,我们通过学习可以知道一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。其中虚基类指针就指向虚基类表。

    而虚表中就记录着直接继承类相对于自己的偏移量,和虚基类相对直接继承类的偏移量。

     

    我们可以用代码来验证一下

    #include <iostream>
    using   namespace    std;
    class B
    {
            public:
                    int _b;
    };
    class C1:virtual public B
    {
            public:
                    int _c1;
    };
    class C2 :virtual public B
    {
            public:
                    int _c2;
    };
    class D : public C1, public C2
    {
            public:
                    int _d;
    };
    
    int main()
    {
            D  d;
            d._b=1;
            d._c1=2;
            d._c2=3;
            d._d=4;
            cout<<*(int*)&d<<endl;
            cout<<*((int*)&d+1)<<endl;
            cout<<*((int*)&d+2)<<endl;
            cout<<*((int*)&d+3)<<endl;
            cout<<*((int*)&d+4)<<endl;
            cout<<*((int*)&d+5)<<endl;
    
            cout << sizeof(B) << endl;       //4
            cout << sizeof(C1) << endl;      //12
            cout << sizeof(C2) << endl;      //12           
            cout << sizeof(D) << endl;       //24
            return 0;
    }
    

     

    运行一下

     

     
    [root@localhost C++]# ./a.out
    134515356
    2
    134515368
    3
    4
    1
    4
    12
    12
    24
    

     

     

    可见我们的猜想是正确的

     

     

    我们可以画出对象模型

    这就是虚拟继承的对象模型

    可以看出虚基类在对象模型的最下面。

    这样的模型有什么好处呢,于普通继承有有什么差别

    1,从访问方面

    普通继承直接访问

    虚拟继承通过虚基类表中的偏移量来访问(这是解决问题的关键)

    2,构造函数

    普通继承没有系统合成的构造函数

    虚拟继承有系统合成的构造函数将偏移量表格地址放对象前四个字节,多一个参数检测是否为虚拟继承。

    3,对象模型

     

    普通继承基类在最上,继承类在下。

    虚拟继承(虚)基类在最下,而派生类在上。

    如图

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • C++ 虚拟继承问题

    千次阅读 多人点赞 2019-08-19 21:19:53
    文章目录前言为什么要有虚拟继承虚拟继承和普通继承的区别虚拟继承 sizeof() 问题 前言 本篇文章主要描述虚拟继承中内存分布情况,当然,可能会不太完全正确,希望大家多多指正 为什么要有虚拟继承 我们看下面一种...

    前言

    本篇文章主要描述虚拟继承中内存分布情况,当然,可能会不太完全正确,希望大家多多指正

    为什么要有虚拟继承

    我们看下面一种情景:

    class A
    {
    public:
    	int a;
    };
    
    class B : public A
    {
    public:
    	int b;
    };
    
    class C : public A
    {
    public :
    	int C;
    };
    
    class D : public B, public C
    {
    public:
    	void func()
    	{
    		
    	}
    };
    

    为了更加方便理解,用图表示:
    在这里插入图片描述
    如上图,那么就会出现问题:类D中的成员变量 int a,如果访问a,就会出现二义性问题,到底是B中的a,还是C中的A,并且会造成数据冗余问题。

    其中,对于二义性问题,我们加访问限定即可。例如 C::a;但对于数据冗余问题却没有办法解决。

    于是C++中就引入了虚拟继承

    将代码改为:

    class A
    {
    public:
    	int a;
    };
    
    class B : virtual public A
    {
    public:
    	int b;
    };
    
    class C : virtual public A
    {
    public :
    	int C;
    };
    
    class D : public B, public C
    {
    public:
    	void func()
    	{
    		
    	}
    };
    

    如果,类中带有虚函数。内存结构会变为怎样??

    class A
    {
    public:
    	A(int v = 100) :X(v) {};
    
    	virtual void foo(void) {}
    
    	int X;
    
    };
    
    class B :virtual public A
    {
    public:
    	B(int v = 10) :Y(v), A(100) {};
    
    	virtual void fooB(void) {}
    
    	int Y;
    
    };
    
    class C : virtual public A
    {
    public:
    	C(int v = 20) :Z(v), A(100) {}
    
    	virtual void fooC(void) {}
    
    	int Z;
    
    };
    
    class D : public B, public C
    {
    	D(int v = 40) :B(10), C(20), A(100), L(v) {}
    
    	virtual void fooD(void) {}
    
    	int L;
    
    };
    

    在这里插入图片描述
    对于类A,内存布局:
    在这里插入图片描述
    对于类B,内存布局:
    在这里插入图片描述
    对于类D,内存布局:
    在这里插入图片描述

    虚拟继承和普通继承的区别

    class A
    {
    public:
    	virtual void funA();
    };  
    
    class B : public A
    {
    public:
    	virtual void funB();
    }
    
    
    class A
    {
    public:
    	virtual void funA();
    };  
    
    class B : virtual public A
    {
    public:
    	virtual void funB();
    }
    

    上述两种继承有什么区别??一种普通继承,一种虚拟继承

    首先,我们知道,如果一个类中有虚函数,那么就会有一个虚表,有一个指针指向这个虚表。
    对于A,内存布局如下:
    在这里插入图片描述
    对于普通继承,继承的虚函数和本有的虚函数共用同一个虚表
    则普通继承 B布局如下:
    在这里插入图片描述

    但对于虚拟继承来说,不管是基类还是派生类都需要有一个指针来维护自己的虚表,并且还要有一个指针指向虚基表,其中存放偏移量
    在这里插入图片描述

    虚拟继承 sizeof() 问题

    有了上面的基础,来看几道题:

    //第一种情况
    class a         
    {            
        virtual void func();
    };           
    class b:public virtual a
    {            
        virtual void foo(); 
    };           
    
    //第二种情况
    class a           
    {              
        virtual void func();
     };             
     class b :public a     
    {              
         virtual void foo(); 
     };                         
    
    //第三种情况
    class a         
    {            
       virtual void func();
       char x;       
    };            
    class b:public virtual a 
    {          
    	virtual void foo();
    };            
    
    //第四种情况
     class a
     {
       virtual void func()
        char x;
      };
     class b:public a
     {
       virtual void foo();
     };
    
    

    对于每种情况,分别计算 sizeof(a), sizeof(b)的大小
    结果:
    第一种:4,12
    第二种:4,4
    第三种:8,16
    第四种:8,8

    参照上述普通继承和虚拟继承的区别,就知道原因了。

    展开全文
  • 本文简单介绍多态和多重继承、虚拟继承的基本概念。随后重点分析了C++中对象模型之间的差异和运行效率
  • 虚拟继承是C++编程语言里的一种语法,使得派生类如果继承基类多次,但只有一份基类的拷贝在派生类对象中。 虚拟单继承 #include&amp;amp;lt;iostream&amp;amp;gt; using namespace std; class Base//...

    虚拟继承是C++编程语言里的一种语法,使得派生类如果继承基类多次,但只有一份基类的拷贝在派生类对象中。

    虚拟单继承

    #include<iostream>
    using namespace std;
    class Base//定义一个基类
    {
    public:
        void BaseTest()//在基类中定义一个函数
        {
            cout << "Base::BaseTest()" << endl;//让这个函数打印自己的作用域及函数名
        }
        int _base;//在基类中定义一个数据成员
    };
    class Derive : virtual public Base//定义一个派生类虚拟公有继承自基类
    {
    public:
        void DeriveTest()//在派生类中定义一个函数
        {
            cout << "Derive::DeriveTest()" << endl;//让这个函数打印自己的作用域及函数名
        }
        int _derive;//定义一个派生类自己的数据成员
    };
    int main()
    {
        Derive d;//创建派生类对象
        cout << sizeof(d) << endl;//打印出派生类所占内存大小
        d._base = 1;//通过派生类改变继承自Base类的数据成员
        d._derive = 2;//通过派生类改变它自己的数据成员
        return 0;
    }

    程序的运行结果为:
    这里写图片描述
    通过结果我们得知,通过派生类创建的对象d的大小为12个字节,如果是普通的单继承,那通过派生类创建的对象大小应该为8字节,所以我们分析得出,d对象所在的内存中除了它来自基类的数据成员和它自己的数据成员外,还有4个字节存放了程序所要使用的数据,那么这些数据是什么呢?我对该程序进行调试,打开内存窗口,找出派生类对象d所在的地址:
    这里写图片描述
    因为我们在程序中执行了d._base=1; d._derive=2;所以可以知道在这里0x012FF7D0就是继承自基类base的数据成员的地址,0x012FF7CC就是派生类自己的数据成员的地址,这时候发现在派生类对象起始的四个字节依然存放了数据,但是这并不是我存放的,是程序自己存放的,为了直到这个数据到底代表着什么,我以地址的形式继续向深层探索它所指向的内存:
    这里写图片描述
    我们可以看到在它的前四个字节里,保存的数值为0;向后四个字节,保存的数值为8,查阅资料得知,这里的8代表的是继承自Base类的数据成员相对于Derive类起始位置的偏移量。
    ●根据分析,我们可以简单的画出虚拟单继承时的派生类对象模型:
    这里写图片描述

    结论在虚拟单继承时,在派生类对象的头四个字节会生成一个指针,这个指针指向与该派生类的基类相关联的偏移量表格。


    ●假设有一个名为Base的类,我们定义一个C1类让他虚拟单继承自Base类,再定义一个Derive类让它虚拟单继承自C1类,那么这个Derive类的对象模型又会是什么样呢?
    编写代码先定义一个Base类,先让C1类公有虚拟继承自Base类,再定义Derive类,让它也公有虚拟继承自C1类:

    #include<iostream>
    using namespace std;
    class Base//定义一个基类
    {
    public:
        void BaseTest()//在基类中定义一个函数
        {
            cout << "Base::BaseTest()" << endl;//让这个函数打印自己的作用域及函数名
        }
        int _base;//在基类中定义一个数据成员
    };
    class C1 :virtual public Base//定义一个类让他公有虚拟继承自Base类
    {
    public:
        void C1Test()//在类中定义一个函数
        {
            cout << "C1::C1Test()" << endl;//让这个函数打印自己的作用域及函数名
        }
        int _c1;//在类中定义一个数据成员
    };
    class Derive :virtual public C1//定义一个类Derive让它公有虚拟继承自C1类
    {
    public:
        void DeriveTest()//在类中定义一个函数
        {
            cout << "DeriveTest()" << endl;让这个函数打印自己的作用域及函数名
        }
        int _derive;//在类中定义一个数据成员
    };
    
    
    int main()
    {
        Derive d;//创建派生类对象
        cout << sizeof(d) << endl;//打印派生类的大小
        d._base = 1;//通过派生类对象改变继承自Base的数据成员
        d._c1 = 2;//通过派生类对象该变继承自C1类的数据成员
        d._derive = 3;//通过派生类对象改编它自己的数据成员
        return 0;
    }

    运行结果:
    这里写图片描述
    通过运行结果可以得知,派生类对象的大小为20字节。当我们通过普通继承时,经过这样的继承方式继承下来的派生类对象的大小应该是12字节,但是经过虚拟继承,比普通继承多出了8个字节,调试该程序,截取出派生类对象d所在的内存地址:
    这里写图片描述
    因为在程序中执行了d._base=1; d._c1=2; d._derive=3;所以可以得知0x006FFA3C就是继承自Base类的数据成员所在的地址,0x006FFA44就是继承自C1类的数据成员的地址,0x006FFA38就是Derive类自己的数据成员所在的地址。在派生类对象里,有两个位置的数据我们不了解是用来做什么的,我们把这两个数据当作指针,分别来探索它们指向的位置:
    ●查看指针0x0012CC80所指向的位置:
    这里写图片描述
    经过分析,可以看出在Derive类对象的起始位置的地址指向了一个表格,这个表格前四个字符保存的是该表格地址相对于Derive类对象起始位置的偏移量0,向后四个字节存放的是继承自Base类模型的数据成员的地址相对于Derive类的起始位置的偏移量8,再向后四个字节存放的是继承自C1类模型的未知指针及数据成员的地址相对于Derive类对象起始位置的偏移量12。
    ●查看指针0x0012D180所指向的位置:
    这里写图片描述
    在Derive类对象的内存中也存在C1类的模型,0x0012D180就是C1类模型的头四个字节,经过分析,可以看出它里面保存着继承自Base类的数据成员相对于Derive类对象中C1类模型的起始位置的偏移量-4。
    Derive类对象的模型为:
    这里写图片描述
    结论:当Derive类作为派生类虚拟单继承一个C1类时(C1类也是经过虚拟单继承继承自一个普通的Base类),Derive类对象的头四个字节会生成一个指针,这个指针指向一个与C1类和Base类相关的偏移量表格,并且Derive类不会将Base类的数据成员看作C1类的数据成员,而且C1类模型的偏移量表格也会被Derive类继承。在C1类模型的偏移量表格中关于Base类的偏移量也会进行相应的更新。


    多重虚拟继承

    #include<iostream>
    using namespace std;
    class Base1//定义Base1类
    {
    public:
        int _base1;//定义一个数据成员
    };
    class Base2//定义Base2类
    {
    public:
        int _base2;//定义一个数据成员
    };
    //定义Derive类;让Derive类公有虚拟继承自Base1类和Base2类
    class Derive :virtual public Base1, virtual public Base2
    {
    public:
        int _derive;//定义一个数据成员
    };
    int main()
    {
        Derive d;//创建Derive类的对象
        cout << sizeof(d) << endl;//打印Derive类对象的大小
        d._base1 = 1;//通过派生类对象改变继承自Base1类数据成员的值
        d._base2 = 2;//通过派生类对象改变继承自Base2类数据成员的值
        d._derive = 3;//通过派生类对象改变他自己的数据成员
        return 0;
    }

    运行结果:
    这里写图片描述
    通过运行结果可以得知,派生类对象所占大小为16字节。调试该程序,查看派生类对象所在的内存:
    这里写图片描述
    因为在程序里我们执行了d._base1=1; d._base2=2; d._derive=3;所以可以看出0x00F699C就是继承自Base1类的数据成员的地址,0x00F6F9A0就是继承自Base2类的数据成员的地址,0x00F6F998就是Derive类自己的数据成员的地址,0x00F6F994指向的内存里存放着一个指针,这个指针指向与Base1类和Base2类相关的偏移量表格。我们打开该表格所在的地址:
    这里写图片描述
    经过分析,可以简的画出Derive类的对象模型:
    这里写图片描述


    菱形虚拟继承

    因为类的对象里并不会包含普通函数的信息,所以从这里开始,例子中将不再定义普通函数。

    #include<iostream>
    using namespace std;
    class Base//定义一个base类
    {
    public:
        int _base;//定义一个数据成员
    };
    class C1:virtual public Base//定义C1类虚拟继承自Base类
    {
    public:
        int _c1;//定义一个数据成员
    
    };
    class C2 : virtual public Base//定义C2类虚拟继承自Base类
    {
    public:
        int _c2;//定义一个数据成员
    };
    //定义Derive类公有虚拟多继承自C1类和C2类
    class Derive : virtual public C1, virtual public C2
    {
    public:
        int _derive;//定义一个数据变量
    };
    int main()
    {
        Derive d;//创建Derive类对象
        cout << sizeof(d) << endl;//打印Derive类对象的大小
        d._base = 1;//通过Derive类对象改变Base类的数据成员
        d._c1 = 2;//通过Derive类对象改变C1类对象的数据成员
        d._c2 = 3;//通过Derive类对象改变C2类对象的数据成员
        d._derive = 4;//通过Derive类对象改变它自己的数据成员
        return 0;
    }

    运行结果:
    这里写图片描述
    用虚拟菱形继承的方式没有再出现我们用普通菱形继承时访问对象不明确的错误,这就说名虚拟继承可以解决普通菱形继承二义性的问题,通过运行结果,得知Derive类对象的大小为28字节,对程序进行调试,取出Derive类对象所在的地址:
    这里写图片描述
    因为在程序中执行了d._base=1; d._c1=2; d._c3=3; d._derive=4;所以可以得知0x00EFF7AC就是继承自Base类的数据成员的地址,0x00EFF7B4就是继承自C1类的数据成员的地址,0x00EFFBC就是继承自C2类的数据成员的地址,在虚拟菱形继承的对象模型里,只有唯一的Base类成员。经过分析,0x00EFF7A4就是Derive类模型关于Base类数据成员和C1类模型和C2类模型的偏移量列表的地址,0x00EFF7B0就是C1类模型关于Base类数据成员的偏移量列表的地址,0x00EFF7BC就是C2类模型关于Base类数据成员的偏移量列表的地址。
    查看Derive类的偏移量列表:
    这里写图片描述
    查看C1类模型的偏移量列表:
    这里写图片描述
    查看C2类模型的偏移量列表:
    这里写图片描述
    所以我们可以简单画出菱形虚拟继承下,Derive类的对象模型:
    这里写图片描述
    通过上面的例子,我们发现虚拟继承是令某个类做出声明,承诺愿意共享它的基类,共享的基类称为虚基类,在这种情况下,不管虚基类在继承体系中被继承过多少次,在派生类中都只包含唯一一个共享的虚基类子对象。并且可以得知他们是通过各自的偏移量列表来实现共享的。

    展开全文
  • 继承体系中有单继承、多继承、和菱形继承,(菱形继承其实是多继承的一种特殊情况)。 单继承:一个子类只有一个直接父类时称这个继承关系为单继承继承:一个子类有两个及以上个直接父类称这个继承关系为多继承 ...

    在继承体系中有单继承、多继承、和菱形继承,(菱形继承其实是多继承的一种特殊情况)。

    单继承:一个子类只有一个直接父类时称这个继承关系为单继承
    在这里插入图片描述
    多继承:一个子类有两个及以上个直接父类称这个继承关系为多继承
    在这里插入图片描述
    菱形继承:多继承的一种特殊情况。一个子类有多个直接父类并且这些直接父类的父类是同一个父类
    在这里插入图片描述

    菱形继承带来的问题:

    拿上面菱形继承的图中的例子来看:因为有子类 Assistant 是继承自多个父类的 (Student Teacher),那么多个父类又继承自同一个父类 Person,因此会导致来自总父类这样就引发了两个问题:
    ① 数据冗余:来自 Person 的成员变量有多份(在Assistant中有来自Student和Teacher分别继承自Person的成员变量,这两份是相同的)
    ② 数据二义性:在Assistant中对这些冗余的数据不能直接进行赋值,因为不知道是在给谁赋值

    class Person
    {
    public:
    	string _name; // 姓名
    };
    class Student : public Person
    {
    protected:
    	int _num; //学号
    };
    class Teacher : public Person
    {
    protected:
    	int _id; // 职工编号
    };
    class Assistant : public Student, public Teacher
    {
    protected:
    	string _majorCourse; // 主修课程
    };
    void Test()
    {
    	// Assistant中有两份来自Person继承给子类的成员_name;
    	//这样会有二义性无法明确知道访问的是哪一个
    	Assistant a;
    	a._name = "Mark";
    }
    

    可以看到对 _name 访问不明确导致程序出错。
    在这里插入图片描述
    但是我们可以通过类名访问到不同的类中的成员分别进行赋值。这样就可以具体到给某一个类中成员赋值。比如上面的程序中 Test 可以修改如下:

    void Test()
    {
    	Assistant a;
    	a.Student::_name = "Mark";
    	a.Teacher::_name = "lzl";
    }
    

    在这里插入图片描述
    可以看到已经将来自不同的成员分别进行了赋值。这样就解决的数据二义性的问题。但是,这样并没有解决数据冗余的问题。那么接下来就是祭出菱形虚拟继承的时候了。


    菱形虚拟继承是什么呢?
    我们来看看下面没有虚拟继承的代码:

    class A
    {
    public:
    	int _a;
    };
    class B : public A
    {
    public:
    	int _b;
    };
    class C : public A
    {
    public:
    	int _c;
    };
    class D : public B, public C
    {
    public:
    	int _d;
    };
    

    来看看对象模型中存的是什么:
    在这里插入图片描述
    再看看下面有虚拟继承的代码以及对象模型中存储着什么:

    class A
    {
    public:
    	int _a;
    };
    class B : virtual public A
    {
    public:
    	int _b;
    };
    class C : virtual public A
    {
    public:
    	int _c;
    };
    class D : public B, public C
    {
    public:
    	int _d;
    };
    

    对象模型:
    在这里插入图片描述
    可以看出通过菱形虚拟继承的对象模型中多出了几行不知道是什么的数据!!!
    其实那就是一串地址:指向了虚基表的位置。
    那么虚基表中存的是什么呢?
    虚基表中存放的是一个偏移量,一个当前位置相对于公共父类成员的偏移量。
    这样就可以通过偏移量来找到公共父类成员的位置并对其进行操作。

    为什么菱形虚拟继承后的对象模型有 24 个字节,而菱形继承的对象模型有 20 个字节,看起来菱形虚拟继承的数据成员还比虚拟继承的数据成员多。
      其实这是因为我们这里用的数据量比较小的原因。当继承下来的数据变多时,那么差距就显示出来了,虚拟继承中的 B 和 D 中都会有一份继承自公共父类的数据成员,这样就有了两份相同的数据。
      而菱形虚拟继承中只是在 B 和 D 中多出了四个字节的虚基表的地址。继承自公共父类的数据成员只有一份。这样就解决了数据冗余的问题了。

    那么为什么需要让 B 和 D 找到属于自己的继承自公共父类的成员呢?
    原因就是为了在赋值的时候能够执行一个完整的切片的操作:看下面的代码:

    D d;
    B b = d;
    C c = d;
    

    就是为了让最终的子类给父类赋值时,要能够找出 B 或 C 成员中继承自 A 的成员,这样才能正确赋值。


    is-a 和 has-a 的区别:

    public继承就是一种 is-a 的关系,因为子类继承了父类,也就是说每个子类对象的都是父类对象。类继承允许根据其他类来定义一个类,也就是说通过父类来生成一个子类。这种生成子类的复用通常被称为 “ 白盒复用 ” 。“白盒” 是针对于可视性而言的,父类内部的细节实现在子类中是可见的。

    组合就是一种 has-a 的关系,在B对象中组合另一个A对象,那么每个B对象都有了一个A对象。这要求A对象具有良好定义的接口。这样就是一个 “ 黑盒复用 ”,在B中并不能看到A的细节实现,只用到了A的接口。

    那么继承和组合到底用哪个好呢?
    其实主要还是根据需求来判断使用哪个。
    继承的缺点:可以看做是破坏了封装,因为在子类中暴露的父类的实现细节。这样本来就违反了面向对象的三大特性之一。而且基类和派生类之间还有很强的依赖关系,耦合度高,这样也违反了 “ 高内聚,低耦合 ” 的特性。
    而组合相比于继承来说并没有破坏封装,并且对象之间也没有很强的依赖性。这样针对于用户也比较友好。

    总而言之,能使用组合就使用组合,尽量不要使用继承,除非你知道自己在做什么!

    展开全文
  • C++的虚拟继承机制

    2021-01-20 03:49:31
    比如:虚函数必须要通过一种间接的运行时(而不是编译时)机制才能够激活(调用)的函数,而虚继承也是必须在运行时才能够进行定位访问的一种体制。存在,但间接。其中关键在于存在、间接和共享这三种特征。
  • //用C语言模拟实现继承继承继承权限public->protected->private)继承是面向对象复用的重要手段,通过继承定义一个类,继承是关系类型之间的建模, 共享共有的东西,实现各自本质不同的东西。在继承关系当中,派生...
  • 深入探究C++虚拟继承

    2018-05-23 10:45:02
    C++的继承作为它的一个特性,必须要做到深入了解,对于C++继承方式,我们之前讲过,有单继承,多继承,菱形继承,虚拟继承,菱形虚拟继承。 这次我们来深入探究一下虚拟继承和菱形虚拟继承。不过在此之前,先让我们...
  • c++虚拟继承

    2016-02-16 21:53:22
    虚拟继承 《c++ primer 3th》p813 在缺省情况下,C++中的继承是按值组合的一种特殊情况。当我们写: class Bear : public ZooAnimal { ... }; 每个Bear 类对象都含有其ZooAnimal 基类子对象的所有非静态数据...
  • 菱形继承中的虚函数表 class A { public: virtual void func1() { cout << "A::func1" << endl; } private: int _a; }; class B :public A{ public: virtual void func1() { cout << "B::func...
  • 虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;... 其实虚拟继承和虚拟函数是完全没关系的,比如B继承自A,C继承自A...
  • 先看第一种简单的情形,所有类中没有任何虚函数的菱形继承。  下面是c++源码: class Top {//虚基类 public:  int i;  Top(int ii) {  i = ii;  } }; class Left : public virtual Top { public: ...
  • 继承分为:单继承、多继承和菱形继承继承:一个子类只有一个直接父类时称这个继承关系为单继承。 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。 菱形继承:菱形继承是多继承的一种...
  • 菱形虚拟继承详解

    2017-04-16 18:48:54
    菱形继承是继承里面比较复杂的有一种,在这里我分别对菱形继承、菱形虚拟继承、菱形虚拟继承含虚函数这几种情况 要清楚虚表和虚基表是两个完全不同的概念 虚表:虚函数表,存的是虚函数->多态 虚基表:存的是偏移量...
  • C++中的继承体系,有单继承、多继承、菱形继承、菱形虚拟继承,以及各类型的对象模型,我们今天做一个简单的剖析 (1)什么多态? 所谓多态,就是“多种形态”。在面向对象的方法中一般是这样描述多态的:向不同的...
  • 虚基类与虚拟继承

    2016-04-13 19:49:51
    虚拟继承: 顾名思义就是继承了但不给开辟空间;class B0 //此段代码中B0就是虚基类 { public: B0() : m(0) {} public: int m; };class B1 : virtual public B0 //B1虚拟继承B0 { public : B1() : n(0) {} ...
  • c++虚拟继承的一个典型例子,通过这个例子可以让初学者从根本体会到虚拟继承是怎么回事
  • C++ 虚拟继承与虚基类

    千次阅读 2015-11-13 22:58:35
    1.多重继承带来的问题C++虚拟继承一般发生在多重继承的情况下。C++允许一个类有多个父类,这样就形成多重继承。多重继承使得派生类与基类的关系变得更为复杂,其中一个容易出现问题是某个基类沿着不同的路径被派生类...
  • C++虚拟继承

    2019-03-03 01:20:47
    // C++的多重继承和虚拟继承 #include &lt;iostream&gt; #include &lt;string&gt; #include &lt;cstring&gt; using namespace std; class Pen { protected: float ...
  • 虚拟继承和虚表

    2016-01-28 13:42:07
    普通继承和虚拟继承
  • 虚拟继承剖析

    2017-04-18 18:03:28
    但是由于多重继承时,在派生类中访问基类成员路径不明确,而经常出现程序出错,为了解决这种错误,我们除了可以加作用域限定符来明确路径外,c++也提供采用虚拟继承来解决此问题。。这里我们用菱形继承为例,先不用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 159,822
精华内容 63,928
关键字:

虚拟继承