C++知识第三篇之继承

这篇具有很好参考价值的文章主要介绍了C++知识第三篇之继承。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C++继承

继承是面向对象编程的重要特征,是对类设计层次的复用

一.介绍

1.继承定义

class Parent
{};

class Child :public Parent
{};

Parent:是父类,也称作基类(base class);Student:是子类,也称作派生类(derived class)

public:表示继承方式为公共继承

2.继承方式

子类继承父类,有3种继承方式

继承方式的作用是:

对于父类非private的成员,使其在子类中的访问权限为MIN(继承方式,父类成员访问限制符)

如:
C++知识第三篇之继承
private成员pc在子类中是不可见的,不能在子类中被访问。

  • public继承

    最常用的继承方式,不改变父类成员在子类的访问权限

  • protected继承

    可将父类的public成员,在子类中为protected

  • private继承

    可将父类的public、protected成员,在子类中为private

这个时候可以让我们回顾一下,类中访问限制符的作用

  1. public(公有):其修饰的成员可以在类外直接访问
  2. protected(保护)与private(私有):其修饰的成员不能在类外直接访问

此时就可以发现protected与private的异同了:

如果成员不想在类外被直接访问,则可以用protected或private修饰。但如果需要在子类中被访问,则需要设置为protected。因为private修饰的成员在子类中是不可见(虽然被子类继承了,但是子类不能访问)。

3.class与struct

  • 用class作为类声明的关键字

如果派生类是使用class关键字,则默认继承(不显示表明)方式为private
C++知识第三篇之继承

  • 用struct作为类声明的关键字

如果派生类是使用struct关键字,则默认继承(不显示表明)方式为private
C++知识第三篇之继承

无论使用那种,最好显示的写出继承方式

二.作用域

不同类都有其自己的类域,因此基类和派生类都有独立的作用域

例如,上例中的Children类,其继承了Parent类,并继承得到Parent类中的成员,但是这些成员却不在Children的作用域里。

这很好理解,毕竟有两个{},基类成员都在基类的类体中声明(定义)。其次,我们可以在派生类中,声明(定义)与基类同名的成员。(要知道在相同作用域中,定义同名的变量是会引起命名冲突的)


如果派生类和基类中有同名成员,派生类成员将屏蔽对基类同名成员的直接访问,即会优先访问派生类的成员,也称隐藏重定义

此时访问基类成员需要显示指定基类的作用域

1.成员变量

C++知识第三篇之继承

c.pa:访问的是Children中的成员pa c.Parent::pa:访问到Parent中的成员pa

2.成员函数

需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
C++知识第三篇之继承


C++知识第三篇之继承

Parent中的show()和Children中的show(),有不同的参数列表,但是并不构成函数重载,因为函数重载的条件是需要在相同作用域中。这两个函数构成隐藏关系

tips:实际中最好不要在继承体系里定义同名成员


三.赋值转换

public继承方式下

派生类的对象可以赋值给基类的对象、基类的指针、基类的引用

这种赋值操作又被叫做切片或者切割,比喻将派生类对象中基类的那部分切给基类进行赋值。

但是基类对象不能赋值给派生类对象

1.给基类对象赋值

C++知识第三篇之继承

将对象c中Parent部分切片赋值给对象p

2.给基类对象指针赋值

C++知识第三篇之继承

pp指向对象c中Parent部分的切片

引用:底层也是指针。

四.派生类的默认函数

对于一个空类,经由编译器处理过后,会为它声明一个默认构造函数、一个拷贝构造函数、一个赋值运算符重载、一个析构函数,且这些函数都是public的。称之为默认成员函数。

默认构造函数:

    会先(在初始化列表的位置)调用基类的默认构造函数,完成基类的创建。

构造函数:

    如果基类没有默认构造函数,则需要在初始化列表显示调用基类的构造函数

拷贝构造函数:

    如果基类和派生类的拷贝构造函数都是编译器生成的,会先(在初始化列表的位置)调用基类的拷贝构造函数。

    如果基类显示定义了,则需要在派生类的拷贝构造函数中显示调用,否则不会完成对基类部分的值拷贝(如果不是在参数列表位置调用的,则会先自动调用基类的默认构造函数,如果基类无默认构造,则会报错)

析构函数:

    析构函数基本上都是当对象的生命周期结束后,由编译器自动调用的。在继承体系中,当一个派生类对象需要释放,会先调用派生类的析构函数,再调用其基类的析构函数。

C++知识第三篇之继承

五. 其他

1.友元

友元的目的是打破封装,使protected或private的类成员也可以被类外访问

友元关系不能被继承
C++知识第三篇之继承

友元函数show()并非Parent类的成员函数,只是通过friend关键字同类外产生联系

友元类同理

2.静态

对于基类的静态成员,无论其派生类有多少个,都共用着同一个静态成员
C++知识第三篇之继承

静态函数同理

六.继承

1.单继承

一个派生类只有一个直接基类的关系称为单继承

class Grandparent
{};
class Parent :public Grandparent
{};
class Children :public Parent
{};

Greadparent–>Parent–>Children,单继承

2.多继承

一个派生类有两个或以上直接基类的关系称为多继承

class Father
{};
class Mother
{};
class Children :public Father, public Mother
{};

Children<—Father & Mother,多继承

对基类的初始化顺序,是根据继承的先后顺序
C++知识第三篇之继承

Children类的默认构造函数中,在其初始列表位置,即使先调用Mother(),后Father(),结果依旧是按继承的顺序先构造Father,后Mother。(初始化列表的错误使用示例,最好按照声明的次序条列)


多继承有可能会出现二义性的问题
C++知识第三篇之继承

3.菱形继承

菱形继承是多继承的一种特例

class Grandma
{};
class Father :public Grandma
{};
class Mother :public Grandma
{};
class Children :public Father, public Mother
{};

Father是继承于其Grandma,Mother是继承于其Grandma,

当Children继承Father和Mother后,就间接的继承了两个Grandma对象
C++知识第三篇之继承

二义性问题
C++知识第三篇之继承

数据冗余问题

对于派生类如果只是需要一份基类的成员即可,那么菱形继承也会造成数据冗余。例如上例的Children类间接的继承了两份Grandma。
C++知识第三篇之继承

4.虚拟继承

意义:为解决由菱形继承而导致的数据冗余和二义性问题


示例:

class Grandma
{
public:
    int i;
};
class Father :virtual public Grandma
{};
class Mother :virtual public Grandma
{};
class Children :public Father, public Mother
{};

让Father和Mother,虚拟(virtual)继承Grandma

此时会产生什么变化呢?
C++知识第三篇之继承

可以看见当Father、Mother虚拟继承后,其首位多出来4个字节的空间,存放着某种数据

C++知识第三篇之继承

首位置是一个指针,指针内存放着地址,该地址指向一张表。指针是虚基表指针,表是虚基表

虚基表中存放的是偏移量,通过第二个偏移量可以找到基类成员变量的地址

可以看出Father、Mother在虚拟(virtual)继承后,就隐式的增加了一个虚基表指针的成员(属于派生类Father、Mother),且所占空间额外增加了4个字节。


下面来看Children类的变化
C++知识第三篇之继承

由于Father、Mother都是虚拟继承于Grandma,其各自的虚基表中第二个偏移量都是指向来着于Grandma的成员变量。因此在Children类c中唯有一份Grandma成员变量

解决了数据冗余和二义性问题


在实际中建议避免定义出菱形继承。

七.其他

1.如何定义一个不能被继承的类?

  • 将构造函数私有化

    通过无法实例化来间接使A类无法被继承
    C++知识第三篇之继承

     因此需要提供一个接口GetA,来返回A类型对象

  • 将析构函数私有化

    通过无法实例化来间接使A类无法被继承
    C++知识第三篇之继承

  • final

    c++11,不同于前两种,使用final来修饰类后,如果有继承操作就会报错
    C++知识第三篇之继承

2. 菱形继承中如果合理使用虚拟继承?

在下图的菱形继承中,只对BC类使用虚拟继承,就可以解决数据冗余二义性问题
C++知识第三篇之继承

如果在菱形继承中,每个继承关系都使用虚拟继承,也可以解决问题,但是没有必要,而且还会使空间增大(虚基表指针),并且性能和复杂度都有问题。

🦀🦀观看~~文章来源地址https://www.toymoban.com/news/detail-461623.html

到了这里,关于C++知识第三篇之继承的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】类与对象 第三篇(初始化列表,explicit,static,友元,内部类)

    目录 再谈构造函数 构造函数体赋值 初始化列表 explicit static成员 匿名对象 友元 友元函数 友元类 内部类 拷贝对象时的一些编译器优化 在创建对象时,编译器通过调用构造函数给对象各个成员变量一个合适的初始值 虽然上述构造函数调用之后,对象中已经有了一个初

    2024年02月05日
    浏览(48)
  • c++类开发的第三篇(讲明白友元函数和this指针)

    c++实现了 封装 , 数据 和 处理数据的操作(函数) 是分开存储的。 c++中的 非静态数据成员 直接内含在类对象中,就像c语言的struct一样。 成员函数并不会出现在对象中,而是作为类的一部分存储在代码段中,需要通过对象或对象指针进行调用。成员函数可以访问类的所有成员

    2024年02月21日
    浏览(47)
  • C++ 第三弹继承和多态-类和对象

    目录 1.继承 1.1什么是继承? 1.2语法格式 1.3继承权限 1.4继承概念语法格式 1.5赋值兼容规则 1.6继承体系中的作用域 1.7在继承体系中的构造和析构 1.8静态成员继承 1.9友元的继承 1.10不同继承方式下子类的对象模型 1.11继承和组合 2.多态 2.1什么是多态 2.2多态的分类 2.3实现条件

    2024年02月10日
    浏览(33)
  • C++ 基础知识 五 ( 来看来看 面向对象的继承 上篇 )

    C++ 继承是指派生类(子类)从基类(父类)继承属性和行为的过程。我们可以创建一个新的类,该类可以继承另一个类的数据属性和方法。 在上述代码中,我们定义了一个父类 Person 与一个子类 Student。Student 类继承了 Person 类的属性和方法,包括 name、age、gender 和 eat() 函数

    2024年02月03日
    浏览(96)
  • Vue基础第三篇

    1 checkebox:     -单选     -多选 2 radio     -单选 示例: 2.1 基本购物车 补充: 2.3加减数量版 v-model 之 lazy、number、trim lazy:等待input框的数据绑定时区焦点之后再变化 number:数字开头,只保留数字,后面的字母不保留;字母开头,都保留 trim:去除首位的空格 前后端要打通

    2024年02月08日
    浏览(40)
  • Vue中的MVVM【第三篇】

            MVVM图示  🌈 一、MVVM简介          简单来说: MVVM(M-VM-M) ,一种更好的UI模式解决方案,MVVM通过数 据双向绑定 让数据 自动地双向同步。 M(Model):Model数据模型,json格式数据 V(View):View视图,jsp、html VM(ViewModel):ViewModel视图模型   🌈 二、MVVM详解        我以

    2024年02月04日
    浏览(41)
  • SpringCloud第三篇:GateWay服务网关

          传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关,客户端只能在本地记录每个微服务的调用地址,当需要调用的微服务数量很多时,它需要了解每个服务的接口,这个工作量很大。那有了网关之后

    2024年02月08日
    浏览(48)
  • Java语言第三篇集合

    集合和数组的对比 数组的长度是不可变的,集合的长度是可变的。 数组可以存基本数据类型和引用数据类型。集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类。 Collection 集合 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素

    2024年01月23日
    浏览(72)
  • Solidity 合约安全,常见漏洞(第三篇)

    如果你只处理受信任的 ERC20 代币,这些问题大多不适用。然而,当与任意的或部分不受信任的 ERC20 代币交互时,就有一些需要注意的地方。 ERC20:转账扣费 当与不信任的代币打交道时,你不应该认为你的余额一定会增加那么多。一个 ERC20 代币有可能这样实现它的转账函数,

    2024年02月09日
    浏览(41)
  • 第三篇|金融人数据来源有哪些

       数据对于金融行业真的很重要,那么金融人有哪些途径查数据呢? 国内: 1. 国家统计局 这个应该是无论什么行业都使用最频繁的网站,每个月都会固定发上个月资产投资数据 、工业增加值和利润数据等常规数据,其他数据也会有,但更新会延迟,可能借助其他网站。

    2024年02月12日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包