第六层:继承

news/2024/10/18 22:31:57/

文章目录

  • 前情回顾
  • 继承
    • 继承的作用
    • 继承的基本语法
      • 继承方式
        • 公共继承
        • 保护继承
        • 私有继承
    • 继承中的对象模型
    • 继承中的构造和析构顺序
    • 继承中同名成员访问
      • 非静态成员
      • 静态成员
    • 多继承
      • 语法
      • 注意
      • 多继承中的对象模型
      • 多继承父类成员名相同
    • 菱形继承
      • 概念
      • 菱形继承出现的问题
        • 虚继承
  • 步入第七层
  • 本章知识点(图片形式)

🎉welcome🎉
✒️博主介绍:一名大一的智能制造专业学生,在学习C/C++的路上会越走越远,后面不定期更新有关C/C++语法,数据结构,算法,Linux,ue5使用,制作游戏的心得,和大家一起共同成长。
✈️C++专栏:C++爬塔日记
😘博客制作不易,👍点赞+⭐收藏+➕关注

前情回顾

在第五层中,我遇到了在C++中第二个重载,操作符重载,它与函数重载差别很大,对于操作符重载,本质其实为函数,只是将函数名换成了C++提供给程序员特定的名字,同时第五层的力量也掌握到了,要踏入第六层了…

继承

当脚接触到第六层的地面的时候,那到声音随之飘来:“这第六层可不简单,它的力量是面向对象的第二大特性——继承,希望你不要让我失望…”

继承的作用

继承是面向对象的三大特性之一,在C++中,有些类的关系如下图:
在这里插入图片描述
可以发现,这些类,下级的成员除了会拥有上级的特性,还拥有自己的特性,这个时候如果一个一个封装,会造成很多重复的代码,这里就可以用继承的力量,减少大量的重复代码。

继承的基本语法

在浏览一些网站的时候,比如:csdn、牛客,都会发现,它们都有公共的头部和底部,那我自己的csdn主页来说:
在这里插入图片描述
在这里插入图片描述
不一样的是中间的内容不相同,那如果用普通方法打印一下这个网页的基本内容会是什么样的?

#include<iostream>
using namespace std;class A1
{
public:void head(){cout << "博客主页头部" << endl;}void middle(){cout << "A1的博客内容" << endl;}void  bottom(){cout << "博客主页底部" << endl;}
};
class A2
{
public:void head(){cout << "博客主页头部" << endl;}void middle(){cout << "A2的博客内容" << endl;}void  bottom(){cout << "博客主页底部" << endl;}
};
class A3
{
public:void head(){cout << "博客主页头部" << endl;}void middle(){cout << "A2的博客内容" << endl;}void  bottom(){cout << "博客主页底部" << endl;}
};
void test1()
{A1 a1;A2 a2;A3 a3;a1.head(); a1.middle(); a1.bottom();cout << "=======================" << endl;a2.head(); a2.middle(); a2.bottom();cout << "=======================" << endl;a3.head(); a3.middle(); a3.bottom();
}
int main()
{test1();return 0;
}

在这里插入图片描述
每个都封装成类,看到是可以进行正常的输出的,但是,可以发现,重复代码很多,这些是不必要的,那现在就可以试试继承的写法,继承的基本语法为:

  • class 子类 : 继承方式 父类
    {
    子类的特点
    }
  • 子类也可以被叫做派生类
  • 父类也可以被叫做基类

普通用法中,可以将重复的代码,封装到父类当中,将每个子类特别的成员封装在子类里面,那继承方式是什么?

继承方式

继承方式一共有三种:

  1. 公共继承(public)
  2. 保护继承(protected)
  3. 私有继承(private)

在这里插入图片描述

公共继承

  • 父类所有权限不变继承到子类当中,但是不能访问私有权限的成员

保护继承

  • 父类除了私有权限的其他权限都变成保护权限继承到子类当中,私有权限内成员依然不能访问

私有继承

  • 父类中所有权限变成私有权限继承到子类当中,私有权限内成员依然访问不到

那了解了继承方式后,怎么用继承的方式写出上面的网页呢:

#include<iostream>
using namespace std;class A
{
public:void head(){cout << "博客主页头部" << endl;}void  bottom(){cout << "博客主页底部" << endl;}
};
class A1:public A
{
public:void middle(){cout << "A1的博客内容" << endl;}
};
class A2 :public A
{
public:void middle(){cout << "A2的博客内容" << endl;}
};
class A3: public A
{
public:void middle(){cout << "A2的博客内容" << endl;}
};
void test1()
{A1 a1;A2 a2;A3 a3;a1.head(); a1.middle(); a1.bottom();cout << "=======================" << endl;a2.head(); a2.middle(); a2.bottom();cout << "=======================" << endl;a3.head(); a3.middle(); a3.bottom();
}
int main()
{test1();return 0;
}

在这里插入图片描述

继承中的对象模型

从父类继承过来的成员,那些属于子类?在继承方式当中提到,私有权限是访问不到的?那是否继承到了子类当中?

#include<iostream>
using namespace std;class A
{
public:int _a;
protected:int _b;
private:int _c;
};
class A1 :public A
{
public:int _d;
};
void test1()
{A1 a1;cout << sizeof(a1) << endl;
}
int main()
{test1();return 0;
}

在这里插入图片描述
可以看到,父类内三个int占12,子类一个int占4,加起来正好是16,而输出的结果也是16,说明,父类内所有非静态成员会被继承到子类当中,只是父类内私有权限的成员被编译器藏起来了,不能进行访问,但是确实继承到了子类当中。

继承中的构造和析构顺序

那对于构造和析构呢?是先有的父类还是子类?可以试着用代码进行验证:

#include<iostream>
using namespace std;class A
{
public:A(){cout << "父类构造调用" << endl;}~A(){cout << "父类析构调用" << endl;}
};
class A1:public A
{
public:A1(){cout << "子类构造调用" << endl;}~A1(){cout << "子类析构调用" << endl;}
};
void test1()
{A1 a1;
}
int main()
{test1();return 0;
}

在这里插入图片描述
可以看到,构造为先有父后由子,析构则反过来。

继承中同名成员访问

非静态成员

当子类和父类中出现了非静态同名成员,如何通过子类对象,访问子类或者父类中的非静态同名成员呢?

  • 子类:直接访问
  • 父类:需要加作用域

这里用代码进行验证:

#include<iostream>
using namespace std;class A
{
public:void a(){cout << "父类中调用" << endl;}
};
class A1:public A
{
public:void a(){cout << "子类中调用" << endl;}
};
void test1()
{A1 a1;a1.a();a1.A::a();
}
int main()
{test1();return 0;
}


成员属性的调用也与函数调用一样,可以用代码来进行验证:

#include<iostream>
using namespace std;class A
{
public:A(){_a = 10;}void a(){cout << "父类中调用" << endl;}void a(int a){cout << "父类第二个函数调用" << endl;}int _a;
};
class A1:public A
{
public:A1(){_a = 20;}void a(){cout << "子类中调用" << endl;}int _a;
};
void test1()
{A1 a1;cout << a1._a << endl;cout << a1.A::_a << endl;
}
int main()
{test1();return 0;
}

在这里插入图片描述

那如果在父类中,发生函数重载,子类能直接调用参数不同的那个函数吗?

、#include<iostream>
using namespace std;class A
{
public:void a(){cout << "父类中调用" << endl;}void a(int a){cout << "父类第二个函数调用" << endl;}
};
class A1:public A
{
public:void a(){cout << "子类中调用" << endl;}
};
void test1()
{A1 a1;a1.a(10);a1.A::a();
}
int main()
{test1();return 0;
}

在这里插入图片描述
编译器直接报错,程序是跑不起来的,这里是因为,在子类中出现与父类同名的成员函数,子类的同名函数会直接隐藏掉父类中所有的同名成员函数。

静态成员

静态成员的访问有两种:

  1. 通过对象访问
  2. 通过类名访问

对于这两个方法,第一种与非静态成员是一样的,可以验证一下第二种——通过类名访问:

#include<iostream>
using namespace std;class A
{
public:static void a(){cout << "父类中调用" << endl;}
};
class A1:public A
{
public:static void a(){cout << "子类中调用" << endl;}
};
void test1()
{A1 a1;A1::a();A1::A::a();
}
int main()
{test1();return 0;
}

在这里插入图片描述
静态函数发生函数重载时也与非静态函数发生函数重载一样。

多继承

  • 在C++种,允许一个类继承多个类,这个时候就是多继承

语法

  • class 子类: 继承方式 父类1 , 继承方式 父类2 …

注意

  • 在多继承中,多个父类中可能有同名成员出现,这个时候需要加作用域来区分

多继承中的对象模型

那当子类继承多个父类的时候,每个父类内的非静态成员都会被继承过去吗?通过代码验证一下:

#include<iostream>
using namespace std;class A
{
public:int a;int b;
};
class A1
{
public:int _a;int _b;
};
class AA :public A, public A1
{
public:int _a1;int _b1;
};
void test1()
{AA aa;cout << sizeof(aa) << endl;}
int main()
{test1();return 0;
}

在这里插入图片描述
和普通继承一样,每个父类的非静态成员都会被继承到子类当中。

多继承父类成员名相同

  • 假设现在两个父类,有两个成员名时一样的,那子类能直接访问这个成员吗?
#include<iostream>
using namespace std;class A
{
public:int _a;int b;
};
class A1
{
public:int _a;int _b;
};
class AA :public A, public A1
{
public:int _a1;int _b1;
};
void test1()
{AA aa;aa._a;}
int main()
{test1();return 0;
}

在这里插入图片描述
编译器是直接报错的,是因为编译器不知道这个成员是来自与哪个父类,这个情况在C++中有个专业名词,叫做二义性,这个时候要怎么样才能访问到呢?就需要进行加作用域,假设现在访问A内的_a:

#include<iostream>
using namespace std;class A
{
public:int _a;int b;
};
class A1
{
public:int _a;int _b;
};
class AA :public A, public A1
{
public:int _a1;int _b1;
};
void test1()
{AA aa;aa.A::_a = 10;cout<<aa.A::_a<<endl;}
int main()
{test1();return 0;
}

在这里插入图片描述

菱形继承

概念

  • 两个子类继承了同一个父类,又有一个类,同时继承了两个子类

在这里插入图片描述

菱形继承出现的问题

  • 马和驴都继承了动物的数据,在骡子使用数据的时候,就会产生二义性,骡子继承动物的数据只需要一份,那应该怎么办?

虚继承

对于上面所说的二义性的问题,因为同样的数据只需要一份,这个时候就可以在继承的时候加上关键字:

  • vritual

将继承变成虚继承,这个时候最大的父类就为虚基类,可以用代码试验:

#include<iostream>
using namespace std;class A
{
public:int a;int b;
};
class AA:virtual public A
{};
class AA1:virtual public A
{};
class AAA :public AA, public AA1
{};
void test1()
{AAA a;a.AA::a = 10;a.AA1::a = 20;cout << a.AA::a << endl;cout << a.AA1::a << endl;
}
int main()
{test1();return 0;
}

在这里插入图片描述
发现,两个数据修改输出的结果是一样的,这个时候就是虚继承起到的作用,让将内部数据共享了一个,这个时候最小的子类也可以直接访问到这个属性:

#include<iostream>
using namespace std;class A
{
public:int a;int b;
};
class AA:virtual public A
{};
class AA1:virtual public A
{};
class AAA :public AA, public AA1
{};
void test1()
{AAA a;a.AA::a = 10;a.AA1::a = 20;cout << a.a << endl;
}
int main()
{test1();return 0;
}

在这里插入图片描述
这是为什么呢?那这个时候从父类继承下来些什么?这个时候从父类继承下来一个指针:

  • vbptr

这个指针叫做虚基类指针,它会指向一个叫做:

  • vbtable

虚基类表,虚基类表中有一个偏移量,这个偏移量会指向一个数据,每个子类指向的数据都是同一份,这个时候数据就只会有一个。
在这里插入图片描述

步入第七层

石碑倒下,大片尘土飞扬,尘土散去,只留下一串脚印,通往第七层…

本章知识点(图片形式)

在这里插入图片描述

😘预知后事如何,关注新专栏,和我一起征服C++这座巨塔
🚀专栏:C++爬塔日记
🙉都看到这里了,留下你们的👍点赞+⭐收藏+📋评论吧🙉


http://www.ppmy.cn/news/18520.html

相关文章

【论文翻译】ViTPose: Simple Vision Transformer Baselines for Human Pose Estimation

【论文】https://arxiv.org/abs/2204.12484v3 【github】GitHub - ViTAE-Transformer/ViTPose: The official repo for [NeurIPS22] "ViTPose: Simple Vision Transformer Baselines for Human Pose Estimation" and [Arxiv22] "ViTPose: Vision Transformer F…

让交互更加生动!巧用CSS实现鼠标跟随 3D 旋转效果

简单分析一下&#xff0c;这个交互效果主要有两个核心&#xff1a; 借助了 CSS 3D 的能力 元素的旋转需要和鼠标的移动相结合 本人简单的说一下如何使用纯 CSS 实现类似的交互效果&#xff0c;以及&#xff0c;借助 JavaScript 绑定鼠标事件&#xff0c;快速还原上述效果。 …

代码重构之路 --我的2022年总结

2022年是我正式参加工作的第10个年头&#xff0c;也是我在CSDN上写博客的第11个年头。在这10余年的时间里&#xff0c;虽然在工作上遇到了各种情况&#xff0c;但我一直坚持输出、坚持分享&#xff0c;一共在CSDN上发表了530多篇原创博文。在这些文章中&#xff0c;大部分都是与…

2023-1-21 刷题情况

最少侧跳次数 首先祝大家除夕快乐。 题目描述 给你一个长度为 n 的 3 跑道道路 &#xff0c;它总共包含 n 1 个 点 &#xff0c;编号为 0 到 n 。一只青蛙从 0 号点第二条跑道 出发 &#xff0c;它想要跳到点 n 处。然而道路上可能有一些障碍。 给你一个长度为 n 1 的数组…

奶牛大学(2023寒假每日一题 1)

Farmer John 计划为奶牛们新开办一所大学&#xff01;有 NNN 头奶牛可能会入学。 每头奶牛最多愿意支付 cic_ici​ 的学费。 Farmer John 可以设定所有奶牛入学需要支付的学费。 如果这笔学费大于一头奶牛愿意支付的最高金额&#xff0c;那么这头奶牛就不会入学。 Farmer J…

【Java开发】Spring Cloud 06 :分布式配置管理-Nacos Config

在微服务架构中&#xff0c;我们会使用一个分布式的“配置中心”来管理所有的配置文件和配置项&#xff0c;本章节将介绍 Nacos 配置中心的特性&#xff0c;以及这些特性在微服务体系中所发挥的作用。在 Spring Boot 应用中&#xff0c;我们习惯于使用传统的配置管理方式&#…

MQ消息队列高可用性

RabbitMQ的高可用性 RabbitMQ是比较有代表性的,因为是基于主从(非分布式)做高可用性的。RabbitMQ有三种模式:单机模式、普通集群模式、镜像集群模式。 单机模式,Demo级别的,一般就是本地demo,没人生产用单机模式。 普通集群模式(无高可用性)就是在多台机器上启动多…

软件测试复习09:集成测试、系统测试、验收测试、回归测试

作者&#xff1a;非妃是公主 专栏&#xff1a;《软件测试》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录集成测试集成测试方法自顶向下自底向上系统测试验收测试回归测试集成测试 把模块拼装到一起&#…