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

目录

一、单继承

1、概述

 2、子类的定义形式

3、子类的构造析够顺序

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

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

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

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

​编辑

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

二、多继承

1、多继承的格式

2、多继承中同名成员 

 三、菱形继承

四、虚继承 

1、虚继承的概述

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


一、单继承

1、概述

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

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

 2、子类的定义形式

定义形式:

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

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

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

 以public为例:

3、子类的构造析够顺序

class Base
{
public:
    Base()
    {
        cout<<"父类构造函数"<<endl;
    }
    ~Base()
    {
        cout<<"父类析构函数"<<endl;
    }
};
class Other
{
public:
    Other()
    {
        cout<<"Other构造函数"<<endl;
    }
    ~Other()
    {
        cout<<"Other析构函数"<<endl;
    }
};
class Son :public Base
{
public:
    Other ob;
public:
    Son()
    {
        cout<<"子类构造函数"<<endl;
    }
    ~Son()
    {
        cout<<"子类析构函数"<<endl;
    }
};
void test()
{
    Son ob;
}

 运行结果:

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

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

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

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

#include<iostream>
#include<string>
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、子类和父类 同名成员数据

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

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

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

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

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

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

class Base
{
public:
    void fun()
    {
        cout<<"父类fun函数"<<endl;
    }
    void fun(int a)
    {
        cout<<"父类带一个参数fun函数"<<endl;
    }
    void fun(int a,int b)
    {
        cout<<"父类带两个参数fun函数"<<endl;
    }
};
class Son :public Base
{
public:
    void fun()
    {
        cout<<"子类fun函数"<<endl;
    }
};
void test()
{
    Son ob;
    //类默认优先访问 子类的同名成员
    ob.fun();
    //必须加父类作用域 访问父类的同名成员
    ob.Base::fun(10);//OB.fun(10);会报错
    ob.Base::fun(10,20);
}

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

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

二、多继承

1、多继承的格式

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

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

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

2、多继承中同名成员 

 三、菱形继承

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

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

         

 形象图:

问题: 

         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<<ob.Data;二义性报错
    cout<<ob.Sheep::Data<<endl;
    cout<<ob.Tuo::Data<<endl;
}

四、虚继承 

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

1、虚继承的概述

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

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

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

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

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

dir命令查看文件 

 

 导出单个类的布局:

cl /d1 reportSingleClassLayoutAnimal main.cpp

Sheep布局: 

Tuo布局:

SheepTuo布局:

         虚继承 会在子类中产生 虚基类指针(vbptr) 指向 虚基类表(vbtable), 虚基类表纪录的是 通过该指针访问公共祖先的数据的偏移量。

 注意:

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

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

  • 31
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值