精华内容
下载资源
问答
  • 这样就会出现菱形继承的情况 即基类的属性 两个父类继承 最终子类拥有两个父类相同的基类属性 这样无法区分(可以利用作用域,但是还是出现了资源浪费的情况,和本不该出现两个相同的属性) 利用virtual关键字解决...

    背景:c++允许多继承    注意:尽量避免多继承

    继承会继承父类的所有属性(暂不考虑私有属性)

    这样就会出现菱形继承的情况

    即基类的属性  两个父类继承  最终子类拥有两个父类相同的基类属性  这样无法区分(可以利用作用域,但是还是出现了资源浪费的情况,和本不该出现两个相同的属性)

    利用virtual关键字解决该问题

    #include <iostream>
    using namespace std;
    
    class Animal 
    {
    public:
    	int m_Age;
    };
    
    class Sheep:virtual public Animal 
    {
    
    };
    
    class Cat :virtual public Animal
    {
    
    };
    
    class Godv :public Sheep, public Cat 
    {
    
    };
    
    void test() 
    {
    	Godv gg;
    	gg.m_Age = 10;
    }
    
    int main() 
    {
    	test();
    	return 0;
    }

     

    展开全文
  • 故这是一个菱形问题,为了避免助教类继承两份人员类数据,要使学生类和教师类的继承方式为虚继承(virtual)。 由派生类实现原理,创建一个派生类对象时编译器先创建出一个基类对象,并调用基类构造函数,再创建一个...

    今天写了一道题,就涉及到了3个问题,可谓是收获颇丰。话不多说,先上题目:
    (1)定义人员类Person:
    公有成员:姓名(Name);
    保护成员:性别(Gender),年龄(Age);
    实现构造函数和析构函数
    (2) 从人员类Person派生学生记录类StudentRecord:
    添加公有成员:学号(Number),班级(ClassName),
    添加静态公有成员:学生总人数(TotalCount);
    添加保护成员:平均成绩(Score);
    实现构造函数和析构函数。
    (3) 从人员类Person派生教师记录类TeacherRecord:
    添加公有成员:学院(CollegeName),系(DepartmentName);
    添加保护成员:教龄(Year);
    实现构造函数和析构函数。
    (4)从学生记录类StudentRecord和教师记录类TeacherRecord派生学生助教类TeachingAssistant:
    添加公有成员:辅导课程(LectureName);
    实现公有函数:显示人员信息(Show),屏幕打印 姓名,性别,年龄,学号,班级,学生总人数,平均成绩,学院,系,教龄,辅导课程。
    实现构造函数和析构函数。为检验类间结构设计是否正确,设计函数void SetName(String name)实现更改一名助教的姓名的功能。
    创建一个助教类的对象
    助教
    姓名 性别 年龄 学号 班级 平均成绩 学院 系 教龄 辅导课程
    郑七 男 22 2010123 软20101 89 信息 软件 1 数据结构
    显示其信息。
    调用更改姓名的函数,更改其姓名为“郑八”,并再次显示其信息。

    输入

    输出
    显示构造的信息和更改前和更改后的助教信息

    助教类由学生类和教师类派生而来,学生类和教师类又都是从人员类派生而来。故这是一个菱形继承问题,为了避免助教类继承两份人员类数据,要使学生类和教师类的继承方式为虚继承(virtual)。

    由派生类的实现原理,创建一个派生类对象时编译器先创建出一个基类对象,并调用基类构造函数,再创建一个派生类对象,继承基类里的数据,并调用派生类的构造函数。所以我们给这些类写构造函数的时候只用给类中原本就有的成员初始化,因为从基类继承来的成员已经在创建基类对象时就调用基类构造函数初始化好了。这个过程联系生活也很好理解,先有爸爸,才有儿子。但是销毁对象时析构函数的调用顺序和构造函数的调用恰恰相反,C++敢情是出悲情剧,白发人送黑发人。

    至于学生类里的公有静态成员学生总人数,我个人认为应该是初始化为0,然后在构造函数里,每创建一个学生,就自增1,在析构函数里,每销毁一个学生,就自减1。这样才能实现实时记录学生总人数。

    我本来打算在构造函数里用cin实时接收数据,但是编译运行时我发现C++无法做到接收中文字符,我再看一眼题目,发现原来题目没有输入信息。好吧,这个题目就是坑啊,根本不是让我们真正去思想实现人员管理系统的功能。既然如此,那就只好把数据用C++的字符串流istringsteam来进行传输了。

    另外,我认为,每个类都应该有一个自己的Show函数,来显示成员的信息,要真考虑起来,还有…太多了一言难尽,等到真正去实现的时候再考虑吧,眼下只是要把这题写出来通过OJ测试,所以在此处代码不赘述了。

    接下来附上已提交且通过测试的代码(我认为真正正经去实现的话应该的写法被我注释起来了):
    必要的数据准备。

    #include <iostream>
    #include <string>
    #include <sstream>
    using namespace std;
    
    istringstream is_1("郑七 男 22");
    istringstream is_2("2010123 软20101 89");
    istringstream is_3("信息 软件 1");
    istringstream is_4("数据结构");
    

    人员类的实现。

    class Person
    {
    public:
        string Name;
        Person()
        {
            // cin>>Name>>Gender>>Age;
            is_1>>Name>>Gender>>Age;
            cout<<"Person"<<Name<<"constructed"<<endl;
        }
        ~Person()
        {
            cout<<"Person"<<Name<<"destructed"<<endl;
        }
    protected:
        string Gender;
        int Age;
    };
    
    

    **学生类的实现。**因为没有什么特殊要求,采用public继承。如果希望子类不能随意修改父类的公有成员(即通过对象直接访问),可以采用protected继承。

    class StudentRecord:virtual public Person
    {
        public:
        string Number;
        string ClassName;
        static int TotalCount;
        StudentRecord()
        {
            // cin>>Number>>ClassName>>Score;
            is_2>>Number>>ClassName>>Score;
            TotalCount++;
            cout<<"Student"<<Name<<"constructed"<<endl;
        }
        ~StudentRecord()
        {
            TotalCount--;
            cout<<"Student"<<Name<<"destructed"<<endl;
        }
        protected:
        int Score;
    };
    int StudentRecord::TotalCount=0;
    

    **教师类的实现。**这里也没什么特殊要求,故采用public继承。

    class TeacherRecord:virtual public Person
    {
    public:
        string CollegeName;
        string DepartmentName;
        TeacherRecord()
        {
            // cin>>CollegeName>>DepartmentName>>Year;
            is_3>>CollegeName>>DepartmentName>>Year;
            cout<<"teacher"<<Name<<"constructed"<<endl;
        }
        ~TeacherRecord()
        {
            cout<<"teacher"<<Name<<"destructed"<<endl;
        }
    protected:
        int Year;
    };
    
    

    助教类的实现。 这同样没有什么特殊要求,采用public继承。

    class TeachingAssistant:public StudentRecord,public TeacherRecord
    {
        public:
        string LectureName;
        TeachingAssistant()
        {
            // cin>>LectureName;
            is_4>>LectureName;
            cout<<"teachingassistant"<<Name<<"constructed"<<endl;
        }
        ~TeachingAssistant()
        {
            cout<<"teachingassistant"<<Name<<"destructed"<<endl;
        }
        void Show()
        {
            cout<<"Name:"<<Name<<" Gender:"<<Gender<<" Age:"<<Age<<" Number:"<<Number
            <<" ClassName:"<<ClassName<<" TotalCount:"<<TotalCount<<" Score:"<<Score
            <<" CollegeName:"<<CollegeName<<" DepartmentName:"<<DepartmentName
            <<" Year:"<<Year<<" LectureName:"<<LectureName<<endl;
        }
        void SetName(string name)
        {
            Name=name;
        }
    };
    

    最后是main函数。

    int main()
    {
        TeachingAssistant item;
        item.Show();
        item.SetName("郑八");
        item.Show();
        return 0;
    }
    

    最后的最后,附上正确运行结果。
    在这里插入图片描述

    展开全文
  • 典型的菱形继承案例: 菱形继承问题: 羊继承了动物数据,驼同样继承了动物数据,当草泥马使用数据时,就会产生二义性。 草泥马继承自动物数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就...

    菱形继承概念:

    ​ 两个派生类继承同一个基类

    ​ 又有某个类同时继承者两个派生类

    ​ 这种继承被称为菱形继承,或者钻石继承

    典型的菱形继承案例:
    在这里插入图片描述

    菱形继承问题:

    1. 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
      
    2. 草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
      

    简单来说,就是通过多条途径重复继承了基类中的成员,就是一份数据继承了多份.

    示例:

    class Animal
    {
    public:
    	int m_Age;
    };
    
    //继承前加virtual关键字后,变为虚继承
    //此时公共的父类Animal称为虚基类
    class Sheep : virtual public Animal {};
    class Tuo   : virtual public Animal {};
    class SheepTuo : public Sheep, public Tuo {};
    
    void test01()
    {
    	SheepTuo st;
    	st.Sheep::m_Age = 100;
    	st.Tuo::m_Age = 200;
    
    	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
    	cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;
    	cout << "st.m_Age = " << st.m_Age << endl;
    }
    
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    

    总结:

    • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
    • 利用虚继承可以解决菱形继承问题

    解决办法两种,要么用作用域,要么用虚继承,

    解释虚继承:
    在vs控制命令板中,输入命令查看类分布
    C:\Users\Administrator\source\repos\test02\test02>cl /d1 reportSingleClassLayoutSheepTuo “菱形继承-虚继承.cpp”

    我们可以发现继承的不是父类成员而是虚基类指针,虚基类指针指向虚基类表,表中记录偏移量,通过偏移量找到继承的基类成员变量.
    在这里插入图片描述

    展开全文
  • 如上图,菱形继承。 带来主要问题:子类继承两份相同数据,导致资源浪费以及毫无意义 解决方法:利用虚继承 在A和C继承Base类前加关键字virtual class Base { int age; }; //A继承base class A:virtual ...

    在这里插入图片描述
    如上图,菱形继承。
    带来的主要问题:子类继承两份相同数据,导致资源的浪费以及毫无意义

    解决方法:利用虚继承
    在A和C继承Base类前加关键字virtual

    class Base
    {
    int age;
    };

    //A继承base
    class A:virtual public Base{};
    //C继承base
    class C:virtual public Base{};
    //D继承A和C
    class D:public A, public C{};

    现在,Base中有属性a,如果不加virtual,那么现在进行如下操作:
    D d;
    d.A::age = 100;
    d.C::age = 200;

    这时候,会输出两个值,加上virtual,从A和C继承过来的就只会输出一个值200。

    底层实现如下:
    ±—
    0 | ±-- (base class A)
    0 | | {vbptr}
    | ±–
    4 | ±-- (base class C)
    4 | | {vbptr}
    | ±–
    ±–
    ±-- (virtual base Base)
    8 | age
    ±–

    D::$vbtable@A@:
    0 | 0
    1 | 8 (D(A+0)Base)

    D::$vbtable@C@:
    0 | 0
    1 | 4 (D(C+0)Base)

    从A和C继承下来的是一个vbptr(virtual base pointer 虚基类指针,指向vbtable)指针,指向各自的vbtable,表中记录了一个偏移量,那么A+8,C+4,最后都得到了唯一的数据age,就解决了数据有两份的问题。

    注:看底层关系的命令:
    E : \cpp文件所在文件夹 > cl /d1 reportSingleClassLayoutSon 文件名.cpp
    (用这个打开:Developer Command Prompt)
     

    展开全文
  • 继承和派生--多继承、菱形继承问题及解决方法1 多继承基本语法2 菱形继承问题3 菱形继承解决方法4 虚继承内部工作原理 1 多继承基本语法 class 派生类名 : [继承方式] 基类名1 , [继承方式] 基类名2 { // .... }; ...
  • 菱形继承问题分析及其在C++的解决方法(虚继承)定义面向对象语言都有一种特性–继承 但存在一种场景(多继承)会掉入陷阱,作简单介绍
  • 当在对象编程时,多把类定义和声明放在不同文件中,但是如果在菱形继承时,最顶端基类头文件会被编译两次,所以会导致类型重复定义。 解决方法:一:在VS2015中可以使用#program once宏来限制编译次数。  二...
  • 问题:C++菱形继承产生二义性产生的原因(解决...菱形继承的示意图: //菱形继承产生的二义性 #include &lt;iostream&gt; using namespace std; //祖先类 class R { private: int r; public: R(i...
  • 在实现继承的过程中,若派生类B,C同时继承基类A,派生类D同时继承类B,C这样对于基类A中的成员相当于在D被继承过两次,A,B,C,D整体关系构建成为一个菱形框图,这样就是C++中的菱形继承问题 代码实现 #...
  • //虚拟继承是为了解决多重继承而出现,比如 /* A / \ B C \ / D 如果直接 class A{}; class B : public A{} class C : public A{} class D : public B, public C{} 那么在创建D对象时候,将会生成2...
  • 菱形继承

    2020-12-18 16:20:03
    解决方法: 1.作用域 2.虚继承:虚继承是一种机制,类通过虚继承指出它希望...虚继承解决了在菱形继承体系中子类对象包含多份父类对象数据冗余和浪费空间资源问题,即决菱形继承中二义性和数据冗余问题。 ...
  • 可以说接口存在目的就是为了解决菱形继承问题。我们用例子来去讲解这个问题。 最好办法就是使用多重继承 新建一个宠物类,让猫和狗都去继承宠物类属性和方法。但是这样就会导致一个问题。即菱形继承问题。 ...
  • 菱形继承问题本质上是一种多继承问题。比如我要定义一个Animal类,在此基类基础上衍生出两个派生类Sheep、Tuo,但我又想构造一个SheepTuo类(同时具备Sheep和Tuo属性)。这样一个子类继承多个父类问题是多继承...
  • 情形:当一个类继承多个父类的时候,想要继承父类的__init__方法,可以使用 父类名.__init__...super方法继承可以解决菱形继承的父类二义性问题,super本质上就是使用MRO这个顺序去调用当前类在MRO顺序中的下一个类...
  • 文章目录一、虚基类和虚继承二、虚基类和虚继承出错情况分析三、菱形继承问题 一、虚基类和虚继承 虚基类:被虚继承的类,就称为虚基类。 virtual作用: 1.virtual修饰了成员方法是虚函数。 2.可以修饰继承方式,是虚...
  • 面向对象语言的三大特征:封装、继承、...解决菱形继承造成的问题的方法和原理四、继承和组合 一、继承的概念及定义 1.继承的概念 继承是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特.
  • Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 普通继承: class Base(object): def __init__(self): print “Base init” class Medium1(Base...
  • 问题:由于将下图定义为多继承类型时,子类会发生二义性与...本次试着说明菱形继承的机理(实现方法)按照上图建立多继承,编写代码:classBase { public: virtualvoidfunc1() { cout<<"Base::func1()"<<...
  • 菱形继承与多态

    2017-02-15 22:57:58
    通过查看内存地址,得出一般菱形继承的对象模型为:   从对象模型可以看出,一般的菱形继承存在两个问题:二义性与数据冗余 解决方法:菱形虚拟继承 (2)菱形虚拟继承  菱形虚拟继承代码:在菱形继承...
  • 在前边的博客中我提到过菱形继承的问题,也给出了几种解决菱形继承的方法 https://blog.csdn.net/liu_zhen_kai/article/details/81590467 但是给的方法没有说明原因,这篇博客将会从内存存储模型角度,以及虚继承...

空空如也

空空如也

1 2 3 4 5 6
收藏数 113
精华内容 45
关键字:

菱形继承的解决方法