关闭

(C++)继承、菱形继承和虚继承的那些事儿

标签: 虚继承菱形继承c++c单继承
196人阅读 评论(0) 收藏 举报
分类:

什么是继承?

继承是C++语言的一种重要机制,该机制自动地为一个类提供来自另一个类的操作和数据结构,这使得程序员只需在新类中定义已有类中没有的成分来建立新类。
这里写图片描述
* 继承使得我们得以用一种简单的方式来描述事物
* 面向对象程序设计可以让你声明一个新类作为另一个类的派生。
* 派生类/子类继承它父类的属性和操作。
* 子类同时也声明了新的属性和新的操作,剔除了那些不适合于其用途的继承下来的操作。

继承的工作方式

这里写图片描述

* 实现一个简单的继承

class Person
{
public:
    void Display()
    {
        cout << "LuLaLaLuLaLa" << endl;
    }
protected:
    string _name;
};

class Student:public Person
{
protected:
    int _num;
};

代码实现如图:

这里写图片描述

事实上,继承的内存布局如下所示:

Alt text

继承与转换

  • 子类对象可以赋值给父类对象(切割/切片)。
  • 父类对象不能赋值给子类对象。
  • 父类的指针/引用可以指向子类对象。
  • 子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)。

派生类的构造

在上述简单实现继承的代码中,可以看出我们并没有声明派生类Student的构造函数,根据类的实现机制,派生类对象创建时,将执行其默认的构造函数。
该默认构造函数首先会调用基类的默认构造函数,而如果基类没有默认构造函数的话,但正好匹配默认参数的构造函数,则会调用该构造函数。

  • 派生类可以直接访问基类的保护数据成员,甚至在构造时初始化他们,但一般并不这样做。
  • 派生类会通过基类的接口(成员函数)去访问他们,初始化也是通过基类的构造函数。
  • 这样的好处有:一旦基类的实现有错误,只要不涉及接口,那么基类的修改不会影响派生类的操作。
  • 类与类之间,你做你的,我做我的,以接口作沟通。

单继承 & 多继承

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

    这里写图片描述

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

    这里写图片描述

菱形继承

这里写图片描述

  • 菱形继承内存布局示意图如下所示:

这里写图片描述

  • 由上图可知,在Assistant中有两份Person成员,故产生了二义性的问题。
  • 同时,二义性也称数据冗余。
  • 为了解决二义性和数据冗余的问题,C++引入了虚继承。

    虚继承

    在被继承的类前加一个virtual关键字即可。
    代码示例:

class Person
{
public:
    Person()
    {
        cout << "Person()" << endl;
    }
    ~Person()
    {
        cout << "~Person()" << endl;
    }
public:
    string _name;
};

class Student :virtual public Person
{
public:
    Student()
    {
        cout << "Student()" << endl;
    }
    ~Student()
    {
        cout << "~Student()" << endl;
    }
public:
    int _id;
};

class Teacher : virtual public Person
{
public:
    Teacher()
    {
        cout << "Teacher()" << endl;
    }
    ~Teacher()
    {
        cout << "~Teacher()" << endl;
    }
public:
    int _num;
};

class Assistant : public Student, public Teacher
{
public:
    Assistant()
    {
        cout << "Assistant()" << endl;
    }
    ~Assistant()
    {
        cout << "~Assistant()" << endl;
    }

protected:
    string _ClassName;
};


void Test()
{
    Assistant a;
    a._name = "Evey";
}

运行结果如下所示:

这里写图片描述

值得注意的是,若没有使用虚继承,还可以制定类进行调用,示例如下:

void Test()
{
    Assistant a;
    a.Student::_name = "Evey";
}

运行结果如下所示:

这里写图片描述

观察以上两种方法各自的运行结果图可知,虚继承的效率更高,能更好的解决数据冗余的问题。

综上所述,在解决菱形继承的二义性和数据冗余性问题时,虚继承是最优的解决方法。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:2834次
    • 积分:274
    • 等级:
    • 排名:千里之外
    • 原创:25篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类