[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析

一、单继承

1、概述

        C++最重要的特征是代码重用,通过继承机制可以利用已有的数据类型来定义新的数据类型,新的类不仅拥有旧类的成员,还拥有新定义的成员。

        例如一个B类继承于A类,或称从类A派生类B。这样的话,类A成为基类(父类),类B成为派生类(子类)。派生类中的成员,包含两大部分:一类是从基类继承过来的,一类是自己增加的成员。从基类继承过过来的表现其共性,而新增的成员体现了其个性。

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第1张图片

 2、子类的定义形式

定义形式:

class 父类{};
class 子类:继承方式 父类名
{
    //新增子类数据
};

继承方式:private protected public(推荐) 

公共继承 保持不变,保护继承变保护,私有继承变私有,所有父类私有在子类中不可见。 

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第2张图片

 以public为例:

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第3张图片

3、子类的构造析够顺序

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第4张图片

class Base
{
public:
    Base()
    {
        cout<<"父类构造函数"<

 运行结果:

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第5张图片

4、子类调用成员对象和父类的有参构造

一般情况下,子类 会自动调用 成员对象和父类的默认构造

调用成员对象和父类的有参构造:子类 必须使用有参构造初始化列表 

初始化列表时:父类类名称成员对象对象名

#include
#include
using namespace std;
class Base
{
public:
    int a;
public:
    Base()
    {
        cout << "父类默认构造函数" << endl;
    }
    Base(int a)
    {
        this->a = a;
        cout << "父类有参构造函数" << endl;
    }
    ~Base()
    {
        cout << "父类析构函数" << endl;
    }
};
class Other
{
public:
    int b;
public:
    Other()
    {
        cout << "Other默认构造函数" << endl;
    }
    Other(int b)
    {
        this->b = b;
        cout << "Other有参构造函数" << endl;
    }
    ~Other()
    {
        cout << "Other析构函数" << endl;
    }
};
class Son :public Base
{
public:
    int c;
    Other ob;
public:
    Son()
    {
        cout << "子类默认构造函数" << endl;
    }
    //子类要给成员中b赋值,父类中的a赋值
    //不能直接赋值,父类会先执行存在,调用父类默认构造
    Son(int a,int b,int c):Base(a),ob(b)//用初始化列表,父类写类名称 成员对象用对象名
    {
        this->c=c;
        cout << "子类有参构造函数" << endl;
    }
    ~Son()
    {
        cout << "子类析构函数" << endl;
    }
};
void test()
{
    Son ob(10,20,30);
}
int main(int argc, char* argv[])
{
    test();
    return 0;
}

 5、子类和父类同名成员处理

处理方式:加作用域

1、子类和父类 同名成员数据

        默认优先访问 子类的同名成员,加父类作用域 访问父类的同名成员。

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第6张图片

2、子类和父类 同名成员函数

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第7张图片

 注:子类会重定义父类的同名函数:

重载:无继承,同一作用域,参数的个数、顺序、类型不同 都可重载

重定义:有继承, 子类 重定义 父类的同名函数(参数可以不同)(非虚函数)

        子类一旦 重定义了父类的同名函数(不管参数是否一致),子类中都将屏蔽父类所有的同名函数。必须加父类作用域 访问父类的同名成员。

class Base
{
public:
    void fun()
    {
        cout<<"父类fun函数"<

6、子类不能继承的父类成员

父类的构造析够重载赋值运算符operator= 不能被子类 继承。

二、多继承

1、多继承的格式

class 父类1{};
class 父类2{};

class 子类:继承方式1 父类1, 继承方式2 父类2
{
    //新增子类数据
};

子类拥有父类1,父类2的大部分数据

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第8张图片

2、多继承中同名成员 

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第9张图片

 三、菱形继承

        两个派生类继承同一个基类而又有某个类同时继承者两个派生类,即有公共祖先的继承。这种继承被称为菱形继承,或者钻石型继承。

        最底层的子类 数据 会包含多份公共祖先的数据和基类派生的派生类的数据

         [第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第10张图片

 形象图:[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第11张图片

问题: 

         1、羊继承了动物的数据和函数,鸵同样继承了动物的数据和函数,当草泥马调用函数或者数据时,就会产生二义性,通过加作用域解决。

        2、草泥马继承自动物的函数和数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以,通过虚继承解决。

class Animal
{
public:
    int Data;
};
class Sheep:public Animal{};
class Tuo:public Animal{};
class SheepTuo:public Sheep,public Tuo{};

void test()
{
    SheepTuo ob;
    //cout<

四、虚继承 

        虚继承 解决 菱形继承中 多份公共祖先数据的问题

1、虚继承的概述

        子类虚继承父类 子类只会保存一份公共数据。

        方式:在继承方式 前加virtual修饰

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第12张图片

2、分析虚继承的实现原理(vs端) 

从电脑开始中的目录找找到 Developer Command Prompt for VS 2022 并打开

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第13张图片

在vs复制完整路径后进入对应文件夹。

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第14张图片

dir命令查看文件 

 [第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第15张图片

 导出单个类的布局:

cl /d1 reportSingleClassLayoutAnimal main.cpp

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第16张图片

Sheep布局: 

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第17张图片

Tuo布局:

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第18张图片

SheepTuo布局:

[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第19张图片         虚继承 会在子类中产生 虚基类指针(vbptr) 指向 虚基类表(vbtable), 虚基类表纪录的是 通过该指针访问公共祖先的数据的偏移量。[第五天】C++继承:单继承、多继承、菱形继承和虚继承的深度解析_第20张图片

 注意:

1、虚继承只能解决具备公共祖先的多继承所带来的二义性问题,不能解决没有公共祖先的 多继承的

2、工程开发中真正意义上的多继承是几乎不被使用,因为多重继承带来的代码复杂性远多 于其带来的便利,多重继承对代码维护性上的影响是灾难性的,在设计方法上,任何多继承 都可以用单继承代替

你可能感兴趣的:(c++,开发语言)