精华内容
下载资源
问答
  • 继承体系中有单继承、多继承、和菱形继承,(菱形继承其实是多继承的一种特殊情况)。 单继承:一个子类只有一个直接父类时称这个继承关系为单继承继承:一个子类有两个及以上个直接父类称这个继承关系为多继承 ...

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

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

    菱形继承带来的问题:

    拿上面菱形继承的图中的例子来看:因为有子类 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的接口。

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

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

    展开全文
  • 菱形虚拟继承

    2017-11-20 18:23:15
    继承一个子类只有一个直接父类时称这个继承关系为单继承。class Person { public: void setinfo(int _age, const char *_name) { age = _age; name = _name; }protected: int age; public: string name; };

    单继承

    一个子类只有一个直接父类时称这个继承关系为单继承。

    class Person
    {
    public:
        void setinfo(int _age, const char *_name)
        {
            age = _age;
            name = _name;
        }
    
    protected:
        int age;
    public:
        string name;
    };
    
    class Student:public Person
    {
    public:
        void setinfo(int _age, const char *_name, int _num)
        {
            Person::setinfo(_age, _name);
            num = _num;
        }
    protected:
        int num;
    };
    

    多继承

    一个子类有两个或以上直接父类时称这个继承关系为多继承

    class Student
    {
    protected :
       int _num ;
       string _name;
    };
    class Teacher : public Person
    {
    protected :
    
        int _id ;
    
    };
    class Assistant : public Student, public Teacher
    {
    protected :
        string _majorCourse ;
    };

    菱形继承

    菱形继承就是对单继承和多继承的一个组合

    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 ; // 主修课程
    };

    Student 和Teacher类是对Person类的单继承,而Assistant类则是多继承前两者。
    这里写图片描述

    lass A
    {
    public:
        int _a;
    };
    
    class B1:public A
    {
    
    public:
        int _b;
    };
    class B2 :public A
    {
    public:
        int _c;
    };
    class D :public B1, public B2
    {
    public:
        int _d;
    };
    
    void test()
    {
        D d;
        //d._a= 0;
        d._b = 1;
        d._c = 2;
        d._d = 3;
    }
    
    int main()
    {
        test();
        return 0;
    
    }
    

    这里写图片描述
    这里写图片描述

    这里写图片描述

    但是由派生类模型我们可以看到,Assistant类中有两个Person成员,在对象d的内存中也可以看到由两个随机值,这就存在二义性和数据冗余的问题。

    为了解决菱形继承中的二义性问题,我们引入了虚拟继承。

    class A
    {
    public:
        int _a;
    };
    
    class B:virtual public A
    {
    
    public:
        int _b;
    };
    
    void test()
    {
        B b;
        b._a = 1;
        b._b = 2;
    }
    
    int main()
    {
        test();
        return 0;
    
    }

    从我们学习到的普通继承知识,我们知道,如果是普通继承的话,派生类对象d的大小应该为8,因为只有基类的成员变量_a和派生类自己的的成员_b,
    这里写图片描述

    这里写图片描述

    并且和普通继承不同,普通继承派生类模型是基类在上,派生类在下,这里是基类在下,派生类在上,并且在前面多了四个字节的内容,我们把这个类容当作地址再次
    这里写图片描述
    在这个多了两个数,一个0一个8,我们在上面的代码中在派生类中添加一个变量,_b,

    lass B:virtual public A
    {
    
    public:
        int _b;
        int _b1;
    };
    

    这里写图片描述

    从这里我们可以看出,这个0是派生类对于自己的偏移量,因为不管你类的内容怎么改变这个0都不变,但是下面那个数字会随着你派生类的大小而改变,下面的数是派生类和基类的偏移量,我们把他叫做偏移量表格,

    所以,在虚拟继承中,有一个指向偏移量表格的偏移地址,

    #define _CRT_SECURE_NO_WARNINGS
    
    #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;
    };
    
    void Test1()
    {
        D d1;
        cout << sizeof(D) << endl;
        d1._c1 = 0;
        d1._c2 = 1;
        d1._d = 2;
        d1._b = 3;
    }
    
    int main()
    {
        Test1();
        system("pause");
        return 0;
    }

    这里写图片描述
    这里有两个偏移地址,
    0x012bcb04
    这里写图片描述
    0x012bcf70
    这里写图片描述
    一开始运行程序时遇到的_b不明确的问题也没有出现了。

    展开全文
  • C++中的继承体系,有单继承、多继承、菱形继承、菱形虚拟继承,以及各类型的对象模型,我们今天做一个简单的剖析 (1)什么多态? 所谓多态,就是“多种形态”。在面向对象的方法中一般是这样描述多态的:向不同的...

    C++中的继承体系,有单继承、多继承、菱形继承、菱形虚拟继承,以及各类型的对象模型,我们今天做一个简单的剖析

    (1)什么多态?

    所谓多态,就是“多种形态”。在面向对象的方法中一般是这样描述多态的:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)。
    多态=动态多态+静态多态
    a.关于静态多态:函数重载
    b.关于动态多态:
    构成动态多态的两个必要条件:
    (1)子类对父类的虚函数重写
    (2)函数通过父类得到指针或引用进行传参 

    实现多态的主要技术是虚继承,当满足以上条件时,父类对象调用被重写过得虚函数,那就去父类中寻找;当子类对象调用时,就去子类中寻找重写过后的虚函数,依次来实现多态,即不同的形态。如下图:


    2:多态的对象模型--单继承&多继承? 
    (1)探索单继承对象模型
    当子类与父类构成多态时,子类继承父类的虚表之后,子类虚表里虚函数在内存里的存储情况;具体以下面例子作为分析参考:



    由上图可得,一个类里面只要有虚函数,类的对象就会有虚表,例中第一个虚表中分别是
    Base类中虚函数fun1,fun2的地址,第二个虚表是Drive类继承父类的虚表,明显这个虚表的内容
    发生了变化,分别是Drive类中重写父类的虚函数fun1,继承父类的虚函数fun2,以及子类本身的虚函数
    fun3,fun4,需要注意的是,每个虚表都以0结束
    (2)探索多继承对象模型
    当一个子类继承多个父类时构成多继承,此时子类会继承这些父类的虚表,子类虚表里虚函数在内存里的存储情况
    具体以下面例子(2个父类)作为分析参考:

    由以上例子可得,子类Drive分别继承了父类Base1,Base2,从而子类得到了两个虚表,继承Base1的
    虚表中,存放了自己重写之后的虚函数fun1,继承Base1中的fun2,以及自己的fun3;
    继承Base2的虚表中,存放了自己重写之后的虚函数fun1,以及继承Base2中的fun2
    注意的是,这里子类自己的fun3只放在了第一个虚表中。
    3:多态的对象模型--菱形继承和菱形虚拟继承?
    (1)菱形继承
       顾名思义,菱形继承就是,几个类的继承关系呈菱形状。
    为此,我们举例解释:
    例如,有A类,B类,C类,D类;其中,B类,C类继承了A类,D类继承了B类,C类。继承关系如下:


    代码如下:
    #include <iostream>
    using namespace std;
    class A
    {
    public:
    	virtual void f1()
    	{
    		cout<<"A::f1()"<<endl;
    	}
    	virtual void f2()
    	{
    		cout<<"A::f2()"<<endl;
    	}
    public:
    	int _a;
    };
    
    class B:public A
    {
    public:
    	virtual void f1()
    	{
    		cout<<"B::f1()"<<endl;
    	}
    	virtual void f3()
    	{
    		cout<<"B::f3()"<<endl;
    	}
    public:
    	int _b;
    
    };
    
    class C:public A
    {
    public:
    	virtual void f1()
    	{
    		cout<<"C::f1()"<<endl;
    	}
    	virtual void f4()
    	{
    		cout<<"C::f4()"<<endl;
    	}
    public:
    	int _c;
    };
    
    class D:public B,public C
    {
    public:
    	virtual void f1()
    	{
    		cout<<"D::f1()"<<endl;
    	}
    	virtual void f5()
    	{
    		cout<<"D::f5()"<<endl;
    	}
    public:
    	int _d;
    };
    
    //打印虚函数表
    typedef void(*V_FUNC)();
    
    void PrintVtable(int* vtable)
    {
    	printf("vtable:%p\n",vtable);
    	int** pvtable=(int**)vtable;
    	for(size_t i=0; pvtable[i]!=0;i++)
    	{
    		printf("vtable[%u]:0x%p->",i,pvtable[i]);
    		V_FUNC f=(V_FUNC)pvtable[i];
    		f();
    	}
    	cout<<"------------------------------------\n";
    }
    
    void test()
    {
    	D d;
    	d.B::_a=5;
    	d.C::_a=6;
    	d._b=1;
    	d._c=2;
    	d._d=3;
    	PrintVtable(*(int**)&d);
    	PrintVtable(*(int**)((char*)&d+sizeof(B)));
    }
    
    int main()
    {
    	test();
    	return 0;
    }
    创建一个D类的对象d,下图为查看d对象中存储的成员变量情况,以及虚表指向:

    总结:在普通的菱形继承中,处于最先的D类型的对象d,它继承了B,C,并且B,C中分别保存了
    一份来自继承A的变量;除此之外,B,C还存了虚表指针,通过它可以找到虚表中存的虚函数地址,
    最后,d对象还存放了自己定义的变量和继承B,C自己定义的变量。
    (2)菱形虚拟继承
        菱形虚拟继承就是在普通菱形继承的前提下加了虚继承(本例是,B,C虚继承A)
    创建一个D类的对象d,下图为查看d对象中存储的成员变量情况,以及虚表指向:

    总结:菱形虚拟继承与菱形继承的区别在于,B,C继承A的公共成员a,既不存储在
    B里,也不存储在C里,而是存储在一块公共的部分,而会将B,C相对于这个变量的
    偏移地址(这里的偏移量地址叫做虚基表)存在B,C里。所以说,对象d里的B,C里存放了虚表指针,虚基表指针,
    自己的变量。

    展开全文
  • 菱形虚拟继承详解

    2017-04-16 18:48:54
    菱形继承是继承里面比较复杂的有一种,在这里我分别对菱形继承、菱形虚拟继承菱形虚拟继承含虚函数这几种情况 要清楚虚表和虚基表是两个完全不同的概念 虚表:虚函数表,存的是虚函数->多态 虚基表:存的是偏移量...

    菱形继承是继承里面比较复杂的有一种,在这里我分别对菱形继承、菱形虚拟继承、菱形虚拟继承含虚函数这几种情况

    一、菱形继承

    看下图就是一个菱形继承


    #include<iostream>
    using namespace std;
    
    class A
    {
    public:
    	int _a;
    };
    
    class B : public A
    {
    public:
    	int _b;
    };
    
    class C : public A
    {
    public:
    	int _c;
    };
    
    class D : public C, public B
    {
    public:
    	int _d;
    };
    
    int main()
    {
    	D dd;
    	cout << sizeof(dd) << endl;
    
    	dd.B::_a = 1;
    	dd._b = 3;
    
    	dd.C::_a = 2;
    	dd._c = 4;
    
    	dd._d = 5;
    	B bb;
    	C cc;
    	cout << sizeof(bb) << endl;
    
    	return 0;
    }



    从内存监视口可以发现dd占用20个字节,但是通过视窗口可以明显发现C类和B类继承的同一个A类的成员_a的值不一样,这就产生了二义性,同一个成员怎可么可以同时又两个值呢?

     

    所以为了解决在菱形继承或者多继承中都会出现的二义性和数据冗余的问题,就提出来虚继承,下面来看看虚继承是怎么实现的..

     

    二.菱形虚拟继承

    在子类继承基类时,通过virtual关键字就可以虚继承,下面我们通过内存布局来看看虚继承是怎么处理数据二义性的...

    #include<iostream>
    using namespace std;
    
    class A
    {
    public:
    	int _a;
    };
    
    class B :virtual public A
    {
    public:
    	int _b;
    };
    
    class C :virtual public A
    {
    public:
    	int _c;
    };
    
    class D : public C, public B
    {
    public:
    	int _d;
    };
    
    int main()
    {
    	D dd;
    	cout << sizeof(dd) << endl;
    
    	dd._a = 1;
    	dd._b = 3;
    
    	dd._a = 2;
    	dd._c = 4;
    
    	dd._d = 5;
    	B bb;
    	C cc;
    	cout << sizeof(bb) << endl;
    
    	return 0;
    }


    这里可以发现 虚继承的dd对象和bb对象都比普通继承之前多了四个字节的空间,说明这四个字节的空间肯定和虚继承有关,下面我们看看内存分布来了解虚继承...




    从监视窗口就可以发现C类和B类继承的同一个A类的成员_a的值现在的值是一样的,都是第二次赋值后的值2. 下面我们看看虚继承是怎么实现C类和B类公用一个成员_a



    从上图可以发现对象dd的内存布局与虚继承之前有很大的区别,首先cc对象和bb对象的内存空间中都分别多了一个存储着一个地址的空间,而把它们_a变量放在了成员变量的最底下,使_a成为一个公共的变量.



    那么cc对象内存中的0x011ceae8bb对象内存中的0x011cdbb0这两个地址指向的空间存的是什么呢?看下图这两个地址就是ccbb对象的虚基表地址,虚基表里存储的是偏移量,cc对象和bb对象就可以通过它们自己的虚基表中的偏移量,来找到公共的基类,然后调用A里的_a


    总结:

    1.虚继承解决了在菱形继承里面子类对象包含多分父类对象的数据冗余现象和二义性。

    2.虚拟继承体系看起来好复杂,但是我们实际应用中很少会定义如此复杂的菱行虚拟继承体系,一般不到万不得已不要定义菱形虚拟继承结构体系,虽然解决了二义性,但也带来了数据冗余的现象。



    三.菱形虚拟继承中包含虚函数

    在看看下面代码,我在上面代码的基础上再加了些虚函数,让我我们来理解菱形继承里的虚表

    #include<iostream>
    using namespace std;
    
    //定义一个可以指向对象里函数的函数指针
    typedef void(*func)();
    
    //打印虚函数表
    void printvtable(int* vtable)   
    {
    	cout << "虚表地址>" << vtable << endl;
    
    	for (int i = 0; vtable[i] != 0; ++i)
    	{
    		printf("第%d个虚函数地址:0x%x,->", i, vtable[i]);
    		func f = (func)vtable[i];
    		f();
    	}
    	cout << endl; 
    }
    
    class A
    {
    public:
    	int _a;
    
    	virtual void func1()
    	{
    		cout << "A::func1()" << endl;
    	}
    };
    
    class B :virtual public A
    {
    public:
    	int _b;
    	virtual void func2()
    	{
    		cout << "B::func2()" << endl;
    	}
    	virtual void func1()
    	{
    		cout << "B::func1()" << endl;
    	}
    };
    
    class C :virtual public A
    {
    public:
    	int _c;
    
    	virtual void func1()
    	{
    		cout << "C::func1()" << endl;
    	}
    };
    
    class D : public C,public B
    {
    public:
    	int _d;
    
    	virtual void func1()
    	{
    		cout << "D::func1()" << endl;
    	}
    	virtual void func3()
    	{
    		cout << "D::func3()" << endl;
    	}
    };
    
    int main()
    {
    	D dd;
        B bb;
    	C cc;
    	A aa;
    
    	cout << sizeof(dd) << endl;
    
    	dd._a = 1;
    	dd._b = 3;
    
    	dd._a = 2;
    	dd._c = 4;
    
    	dd._d = 5;
    
    	cout << sizeof(bb) << endl;
    
    	//D类里继承B类的虚表
    	cout << "D:bb:0x" << ⅆ
    	int* vtabledd = (int*)(*(int*)&dd); 
    	printvtable(vtabledd);
    
    	//D类里继承C类的虚表
    	cout << "D:cc:0x" << ((int*)&dd + 3);
    	int* vtablecc = (int*)(*((int*)&dd + 3));
    	printvtable(vtablecc);
    
    
    	//D类里继承A类的虚表
    	cout << "D:aa:0x" << ((int*)&dd + 6);
    	int* vtableaa = (int*)(*((int*)&dd +6));
    	printvtable(vtableaa);
    	return 0;
    }


    要清楚虚表和虚基表是两个完全不同的概念

    虚表:虚函数表,存的是虚函数->多态

    虚基表:存的是偏移量,解决二义性和数据冗余性。

     

    好了,那么我上面我还提出了一个问题,就是虚基表里上面的地址为什么存着一个寻找对象自己的虚表的偏移量?下面我们解决

     

    提醒:因为我代码改动后,再次编译内存中对象的地址数值会变化,但不影响大家看内存是怎样分布的和虚基表的作用。(仅下面这个图的地址数值和上面输出结果不一样)

    下面我们来看看菱形继承中含虚函数后dd对象的内存分布是什么样子?是sizeof(dd)为什么是32看下图就知道了



    从上图可以发现,当给bb对象里加了个fun2函数后发现dd的对象模型又多了四个字节,这四个字节是在dd对象中继承的bb的内存空间中多出来的,就是用来存放bb对象自己的虚表地址。

    因为aa对象被两个对象所继承,所以aa的虚表被被公用,因此bbcc对象的虚函数就不能放在他们公共的虚表里,只能创建自己的虚表,把自己的虚函数放在自己的虚表。

    然后dd对象里自己的虚函数(非继承来的)存在第一个继承来的对象的虚表里。

     

     

    下面看看菱形继承dd对象里的虚函数都分别在什么地方存储的:


    通过上面的内存分布我总结:

    1.dd对象继承重写aa对象的虚函数存储在继承来的aa对象的虚函数表里。

    2.dd对象里自己的虚函数(非继承来的)存在第一个继承来的对象的虚表里。

    3.因为aa对象被两个对象所继承,所以aa的虚表被被公用,因此bbcc对象的虚函数就不能放在他们公共的虚表里,只能创建自己的虚表,把自己的虚函数放在自己的虚表。


    下面我根据总结上面总结画出了一份菱虚拟继承的对象模型



    如有错误之处,请多多指点














    展开全文
  • 菱形继承继承:一个子类只有一个直接父类时称这个继承关系为单继承  多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承 菱形继承菱形继承是多继承的一种特殊情况。    来看看下面这段...
  • 菱形继承 class A { public : A() { /*cout ()" ;*/ } virtual void show() { cout ()" ; } virtual void show1() { cout ()" ; } void show2() { cout ()" ; } protected: int _a; }; ...
  • C++三大特性 继承 封装 多态 继承就是一种管理行为。为了使我们规范的去使用类里面的成员。把成员函数或者成员变量保护起来,必须通过对象去调用。...虚拟继承指针 也叫做虚基表指针。 虚基表其...
  • 什么是菱形继承? 首先,我们看一下单继承,以及多继承是什么? 单继承:一个子类只有一个直接父类时称这个继承关系为单继承 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承 ...虚拟继承...
  • 浅谈菱形虚拟继承

    2017-02-15 20:43:33
    当我们谈到C++时首先会想到什么?...今天我们就浅谈一下菱形虚拟继承。 我们要谈菱形继承,首先要弄明白什么是菱形继承,它的作用是什么。 菱形继承:两个子类继承同一个父类,而又有子类同时继承这两个子类。例如:
  • 菱形继承中的虚函数表 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语言模拟实现继承继承继承权限public->protected->private)继承是面向对象复用的重要手段,通过继承定义一个类,继承是关系类型之间的建模, 共享共有的东西,实现各自本质不同的东西。在继承关系当中,派生...
  • 文章目录继承的概念和定义继承的概念继承的定义代码基类和派生类对象的赋值转换(赋值兼容原则) 继承的概念和定义 继承的概念 继承机制是对象对象程序设计使代码可以复用的重要手段,允许程序员在保持原有类特性的...
  • 一、继承 (一)概念:使代码复用的手段 (二)继承权限: 1.类中的访问权限(public、protected、private) 2.继承列表中的继承权限(public、protected、private) 注意:保护访问权限和私有访问权限在派生类中...
  • 继承是C++三大特性之一,而继承又分为单继承和多继承,将单继承和多继承组合起来就成了复杂的菱形继承,本文重点讲菱形继承带来的问题和如何解决该问题。 ●回顾知识: 单继承:一个子类只有一个直接父类时称这个...
  • 什么是菱形继承? 1、菱形继承是多继承关系的一种特殊情况 2、继承关系如下图: (菱形继承关系图) (基于菱形继承关系的对象模型) 3、菱形继承代码: class B { ...
  • 菱形继承3. 继承的总结和反思4. 常见笔试面试题5. 继承总结 0. 前述 本篇博文承接[C++系列] 67. 继承基础讲解部分,进一步深入讨论更为复杂的菱形继承。 为更进一步加深理解,可参考[C++系列] 68.多态基础及虚函数...
  • 菱形继承菱形继承是多继承的一种特殊情况。 菱形继承的例子: class Person { public: string _name; // 姓名 }; class Student : public Person { protected: int _num; //学号 }; class Teacher : public ...
  • 关于继承的基本概念可以阅读我的上一篇博客:C++中的继承继承 一个子类有两个或以上直接...菱形继承:一种特殊的多继承 菱形继承存在的问题: #include<iostream> using namespace std; class A { public:
  • 菱形继承菱形继承是多继承的一种特殊情况 菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有是数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。 class Person { public: ...
  • 首先在谈到菱形虚拟继承之前先说明一下菱形继承: 菱形继承是多继承的一种特殊情况(如下,画渣勿喷): 图中 B C 两个类都继承了A类,而 B C 又都被 D类继承按照继承的定义,派生类当中都包含了基类,而这时虚拟继承...
  • 一、继承的概念及定义 1、继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新的功能,这样产生新的类,新类称为派生类或基类。继承是类...
  • 基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。 我们看一下下面的代码; #include<iostream> #include<string.h> using ...
  • 举个栗子看看菱形继承 class Person { public: string _name; // 姓名 }; class Student : public Person { protected: int _num; //学号 }; class Teacher : public Person { protected: int _id; // 职工编号 }...
  • 菱形继承以及菱形虚拟继承 什么是单继承:一个子类有一个直接父类,这个继承关系为单继承 什么是多继承:一个子类有两个或两个以上的直接父类,这个继承关系为多继承。 菱形继承是多继承的一种特殊情况 如图: 从上...
  • 目录前言:面向对象(OO)三大特性继承 前言:面向对象(OO)三大特性 继承
  • 面向对象程序设计的核心思想是封装(数据抽象)、继承和多态(动态绑定)。 通过使用数据抽象,我们可以将类的接口与实现分离; 使用继承,可以定义相似的类型并对其相似关系建模; 使用动态绑定,可以在一定程度...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,298
精华内容 1,319
关键字:

菱形虚拟继承