【难学易用c++ 之 继承】

news/2024/4/19 13:47:00/文章来源:https://blog.csdn.net/m0_66363962/article/details/129767712

目录:

  • 前言
  • 一、继承的概念及定义
    • (一)概念
    • (二)继承定义
      • 继承关系和访问限定符
      • 继承基类成员访问方式的变化
  • 二、基类和派生类对象赋值转换
  • 三、继承中的作用域
  • 四、派生类的默认成员函数
  • 五、继承与友元
  • 六、继承与静态成员
  • 七、复杂的菱形继承及菱形虚拟继承
    • 菱形继承
    • 虚继承
    • 组合
  • 补充

前言

打怪升级:第50天
在这里插入图片描述

一、继承的概念及定义

(一)概念

继承机制是面向对象程序设计使代码可以复用的最重要的手段,他允许类在保持原有特性的基础上进行拓展,增加新的功能,这样产生的类叫做子类或者派生类。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。

示例:

class Person
{public:void Print(){cout << "name : " << _name << endl;cout << "age : " << _age << endl;}
protected:string _name = "小李子";int _age = 20;
};//  Person类作为父类,被student 和 teacher继承后,父类的public 和 protected 成员(变量和函数)都会变成子类的一部分,可以在子类中进行访问。
class Student : public Person
{
private:string _s_id = "000"; // 学号
};class Teacher : public Person
{
private:string _t_id = "100"; // 工号
};void Test_person1()
{Student s1;s1.Print();Teacher t1;t1.Print();
}

运行实例:
在这里插入图片描述


(二)继承定义

在这里插入图片描述

继承关系和访问限定符

在这里插入图片描述

继承基类成员访问方式的变化

这里是引用

基类的私有成员(private)子类无论如何都是不可见的,基类的保护成员(protected)和共有成员(public)在子类中的受继承方式限制,一般都使用 public继承,也就都是可见的;
这就好比父亲挣得钱和 父亲藏的私房钱,父亲挣的钱一家人都可以用,但是私房钱只有父亲自己可以用,哪怕是儿子也不行。

这里有一个小问题:子类可以访问到父类的私有成员(私房钱)吗,
我们上面讲:父类的私有成员在子类中不可见,既然不可见(不知道老爸私房钱藏在哪里),那是不是就不能使用?

在这里插入图片描述

  • 总结
  1. 基类private成员在派生类中无论以何种方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类不管来类内还是类外都无法访问到
  2. 基类private成员在派生类中是访问不到的,如果想要在派生类中可以访问,但是在类外不能访问到,就需要使用protected。可以看出:保护成员限定符是因继承才出现的(c++刚问世的时候只设置了私有和共有,在C++2.0时才加入了保护成员)。
  3. 基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private
  4. 使用关键字class时默认的访问和继承方式是private, 使用struct 时默认的访问和继承方式是public,不过最好显示写出继承方式
  5. 在实际运用中一般使用都是public继承,几乎很少使用 protected/private继承。
  6. 一句话总结:权限可以缩小但不可放大(类比:const变量不能被非const变量引用)。

二、基类和派生类对象赋值转换

  • 派生类可以赋值给基类的对象/基类的指针/基类的引用。这里有个形象的说法加切片或者切割
    寓意把派生类中基类那部分切出来赋值过去。

  • 基类对象不能赋值给派生类对象

  • 基类的指针或引用可以通过强制类型转换赋值给派生类的指针或引用。但是只有访问基类成员时才是安全的,否则可能造成越界

这里是引用在这里插入图片描述在这里插入图片描述


三、继承中的作用域

  1. 在继承体系中,基类和派生类都有独立的作用域
  2. 子类和父类中有同名成员子类成员将屏蔽对父类同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类对象中,可以使用 基类::基类成员显示访问)。
  3. 注意:成员函数只要函数名相同就构成隐藏。(区分函数重载:在同一作用域内,函数名相同而参数不同);
  4. 注意在实际的继承体系里面最好不要定义同名的成员。
class Person
{
public:void Print(){cout << "name : " << _name << endl;cout << "age : " << _age << endl;}string _name = "小李子";int _age = 20;
};class Student : public Person
{
public:void Print(){cout << "id :" << _s_id << endl;}string _s_id = "000"; // 学号
};class Teacher : public Person
{
public:void Print(){cout << "id :" << _t_id << endl;}string _t_id = "100"; // 工号
};void Test_Person3()
{Student s1;s1.Person::Print();  // 隐藏,显示调用同名成员s1.Print();cout << endl;Teacher  t1;t1.Person::Print();t1.Print();
}

这里是引用


四、派生类的默认成员函数

6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类
中,这几个成员函数是如何生成的呢?

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认
    的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化
  3. 派生类的operator=必须要调用基类的operator=完成基类的复制
  4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
    保证派生类对象先清理派生类成员再清理基类成员的顺序。
  5. 派生类对象初始化先调用基类构造再调派生类构造。
  6. 派生类对象析构清理先调用派生类析构再调基类的析构。(保证进栈出栈顺序)。
  7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系

这里是引用在这里插入图片描述

class Person
{
public:Person(string name = "小李子", int age = 20):_name(name), _age(age){}void Print(){cout << "name : " << _name << endl;cout << "age : " << _age << endl;}string _name;int _age;
};class Student : public Person
{
public:Student(string name = "小李子", int age = 20, string id = "000"):Person(name, age)  // 显示调用基类的构造, _s_id(id){}Student(const Student& s):Person(s)           //  显示调用, _s_id(s._s_id){}void Print(){Person::Print();cout << "id :" << _s_id << endl;cout << endl;}string _s_id; // 学号
};void Test_Person4()
{Student s1;s1.Print();Student s2("陈平安", 40);s2.Print();Student s3("裴钱", 26, "001");s3.Print();Student s4 = s3;s4.Print();
}

这里是引用

class Base
{
public:Base(int val = 10):b_val(val){cout << "Base()" << endl;}Base(const Base& b):b_val(b.b_val){cout << "Base(const Base&)" << endl;}Base& operator=(const Base& b){if (&b != this){b_val = b.b_val;cout << "Base::operator=()" << endl;}return *this;}~Base() { cout << "~Base()" << endl; }int b_val;
};class Son: public Base
{
public:Son(int val1 = 10, int val2 = 20):Base(val1),s_val(val2){cout << "Son()" << endl;}Son(const Son& b) :Base(b)  //  派生类对象初始化基类对象,s_val(b.s_val){cout << "Son(const Base&)" << endl;}Son& operator=(const Son& s){if (&s != this){Base::operator=(s);  // 显示调用基类的赋值s_val = s.s_val;cout << "Son::operator=()" << endl;}return *this;}~Son() { cout << "~Son()" << endl; }int s_val;
};void Test_p5()
{Son s1;cout << endl;Son s2 = s1;cout << endl;Son s3;s3 = s1;cout << endl;Son s4(1, 2);cout << endl;
}

这里是引用

注意:在派生类的构造和赋值中 如果我们不显示调用基类的赋值和拷贝构造,编译器不会自动调用


五、继承与友元

友元关系不能继承,也就是说基类友元不能访问派生类私有和保护成员。

class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{
protected:int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}
void main()
{Person p;Student s;Display(p, s);
}

这里是引用


六、继承与静态成员

**基类定义了static静态成员,则整个继承体系里面只有这样一个静态成员。**无论派生出多少个子类,都只有一个static成员实例。

class Base
{
public:static int cnt;
};
int Base::cnt = 0;   // 静态成员变量需要在类外初始化,且初始化时不需要加staticclass Son1 :public Base
{
public:Son1(){++cnt;}
};class Son2 :public Base
{
public:Son2(){++cnt;}
};void Test_Static1()
{Son1 s1_1;Son1 s1_2;cout << "cnt =  " << s1_2.cnt << endl;Son2 s2_1;Son2 s2_2;cout << "cnt =  " << s2_2.cnt << endl;
}

这里是引用


七、复杂的菱形继承及菱形虚拟继承

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

菱形继承

菱形继承造成的影响:Child从父类Student中继承了Person类的属性,又从父类Teacher中也继承了Person类的属性,那么此时,Child类中就有了两份Person类属性,
这会有两个问题:二义性和数据冗余

  1. 我们访问Person类属性时不能确定访问的到底是从父类STudent中继承下来的还是从父类Teacher中继承下来的;
  2. 多份的Person类属性会造成数据的冗余。

这里是引用

虚继承

为了解决菱形继承带来的问题,我们祖师爷在C++3.0给出了解决方案:虚继承
关键字:virtual(虚拟)

class A
{
public:int _a;
};class B : virtual public A  //  虚继承
{
public:int _b;
};class C : virtual public A  //  虚继承
{ 
public:int _c;
};class D : public B, public C 
{
public:int _d;
};void Test_1()
{D d1;d1.B::_a = 10;d1.C::_a = 30;}

这里是引用在这里插入图片描述在这里插入图片描述

这里有盆友可能会提出疑问:我们解决数据冗余不就是为了节省空间吗,这里多增加了一个A,而且父类中的A也还存在,并且A中存放的地址还指向另外一块区域,这样不就更加浪费空间了吗,难道我们的祖师爷在当时设计虚继承时“喝了假酒”,所以这部分写出问题来啦?

这里是引用在这里插入图片描述

虚继承原理解析:
在这里插入图片描述

很多人说c++的语法复杂,多继承就是一个体现:有了多继承就会有菱形继承,有了菱形继承就会有菱形虚拟继承,底层实现就很复杂,
并且编译器在背后为我们做的一系列操作也是有时间消耗的,所以一般不建议写出多继承,一定不要写菱形继承!
由于c++现世的时间非常早,当时的参考资料较少,因此设计上难免会有一些不合理的地方(如多继承、string类等等),我们的前辈也在对c++不断的进行缝缝补补,后世的一些OO语言在参考了c++之后就限制了继承的操作,如Java就只有单继承没有多继承。

组合

class A
{
public:int _a1;
protected:int _a2;
};class B :public A
{
public:void Init(){_a1 = 10;_a2 = 20;}
};class C
{
public:void Init(){_aa._a1 = 10;
//		_aa._a2 = 20;   //  不可访问}A _aa;      //   组合 -- c中有a
};

在这里插入图片描述在这里插入图片描述


补充

在这里插入图片描述在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_103072.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用阿里云短信服务实现登录页面,手机验证码登录?

1&#xff1a;个人如何使用阿里云短信服务&#xff1f; 2022如何使用个人阿里云短信服务&#xff1f;_linxiMY的博客-CSDN博客添加完成之后&#xff0c;等待审核&#xff01;一般2个小时就会出来审核结果了&#xff0c;这里我因为注册申请时填写规则有误&#xff0c;足足审核了…

pytest 学习三(前置后置操作)

pytest测试框架_pytest框架-CSDN博客 一、常用的操作 一、setup/teardown 每个用例之前、之后执行 二、setup_class/teardown_class 在每个类之前、之后执行一次 二、pytest.fixture 设置前置后置操作范围 pytest.fixture(scope"",params,autouse,ids,name) 其中 sc…

JavaScript模块化开发

目录&#xff1a; 1 认识模块化开发 2 CommonJS和Node 3 require函数解析 4 AMD和CMD&#xff08;了解&#xff09; 5 ESModule用法详解 6 ESModule运行原理 模块化不是两个不同的js文件直接导入到某个页面中的&#xff0c;因为这两个文件只要有相同的变量或函数&#xf…

R基础函数概览(一)

rep 函数形式&#xff1a;rep(x, time , length , each ,) 参数说明&#xff1a; x&#xff1a;代表的是你要进行复制的对象&#xff0c;可以是一个向量或者是一个因子。 times&#xff1a;代表的是复制的次数&#xff0c;只能为正数。负数以及NA值都会为错误值。复制是指的…

云原生(docker+k8s+阿里云)-Docker

Gitee-Kubernetes学习 kubectl备忘清单 k8s官方文档-task [云原生-kubectl命令详解] ingress详解 ingress官方文档 云原生-语雀-架构师第一课 从Docker到Kubernetes进阶-社区 云计算学习路线-阿里云大学 如上图&#xff0c;服务器有公网ip和私网ip&#xff0c;公网ip是外部访问…

Ubuntu20.04使用多卡训练HyperNetwork模型和LoRA模型全流程及疑难问题解决方案

目录 一. LoRA模型多卡训练1.1 安装xformer等库1.2 设置路径1.3 多卡训练 二. LoRA模型多卡训练疑难报错解决方案多卡训练报错 软硬件配置&#xff1a; CPU: AMD 5800 8core 16Thread GPU: NVIDIA RTX 3090 *1 NVIDIA TITAN RTX *1 OS: Ubuntu20.04 一. LoRA模型多卡训练 1.1 …

一篇终结synchronized

一&#xff1a;基本原理 Java对象在内存中由两部分组成 &#xff1a; 1 是成员变量 2 是对象头&#xff0c;以32位虚拟机介绍&#xff1a;此时对象头是64位&#xff0c;即8字节 其中32个字节代表 mark word 另外32个字节代表klass word分别是什么意思呢&#xff1f; 1 klass …

写题总结1

先把自己写完的总结一下&#xff1a; 题目一&#xff1a; 猫儿园的告示牌上贴着 ab 大小的矩形广告纸。猫猫对广告不感兴趣&#xff0c;她想知道能否用 cd 的矩形白纸完全覆盖这个广告。猫猫可以对白纸进行平移、旋转&#xff0c;但不能折叠或撕开等。如果可以完全覆盖输出 YE…

滴水逆向3期笔记与作业——01汇编

防止OneNote丢失。 海哥牛逼。 01汇编笔记 01进制进制定义10-2进制转换八进制 02数据宽度/逻辑运算数据宽度与存储逻辑运算计算机做加法的本质作业 03通用寄存器_内存读写通用寄存器表通用寄存器图内存读写计算机操作系统位数意义 04内存地址_堆栈寻址公式PUSH指令POP指令作业 …

【IAR工程】STM8S基于ST标准库读取DHT11数据

【IAR工程】STM8S基于ST标准库读取DHT11数据 ✨申明&#xff1a;本文章仅发表在CSDN网站&#xff0c;任何其他网站&#xff0c;未注明来源&#xff0c;见此内容均为盗链和爬取&#xff0c;请多多尊重和支持原创!&#x1f341;对于文中所提供的相关资源链接将作不定期更换。&…

4月有8本SCIE期刊被剔除(附MDPI/Frontiers/Hindawi最新在检期刊)

2023年4月SCI、SSCI期刊目录更新 2023年4月18日&#xff0c;科睿唯安更新了WOS期刊目录&#xff0c;继上次3月WOS期刊目录剔除50本SCIE&SSCI期刊之后&#xff0c;此次4月更新又有8本SCIE期刊发生变动&#xff0c;其中有4本期刊被踢出SCIE数据库&#xff0c;4本期刊更改了名…

流程图拖拽视觉编程--概述

一般的机器视觉平台采用纯代码的编程方式&#xff0c;如opencv、halcon&#xff0c;使用门槛高、难度大、定制性强、开发周期长&#xff0c;因此迫切需要一个低代码开发的视觉应用平台。AOI缺陷检测的对象往往缺陷种类多&#xff0c;将常用的图像处理算子封装成图形节点,如抓直…

Android 系统架构大图

android的系统架构和其操作系统一样&#xff0c;采用了分层的架构。从架构图看&#xff0c;android分为四个层&#xff0c;从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和Linux核心层。 1.应用程序 Android会同一系列核心应用程序包一起发布&#xff0c;该应用…

确定因果随机森林的树木数量 the number of trees

前言 推断因果性和分析异质性是统计学家在处理混杂任务中的圣杯。传统且主流的方法有:倾向性评分、分层分享、比例风险模型等。新的方法也有很多,代表就是:因果随机森林。这种算法,浅看难度一般,深入探索发现坑还是很多的。这篇博客不对算法做深入探讨,仅仅是我在阅读文…

Nautilus Chain :基于模块化架构的Layer3正在走向成熟

Nautilus Chain 是一个基于 Eclipse 和 Celestia 构建的模块化 Layer3 链。作为定位在 Layer0 的链基建概念&#xff0c;Eclipse 和 Celestia 为面向未来的区块链扩容技术提供了一套开发工具和基础框架。尽管这种前沿技术过去一直处于概念验证阶段&#xff0c;尚未推出适用于大…

Java并发(三)----创建线程的三种方式及查看进程线程

一、直接使用 Thread // 创建线程对象 Thread t new Thread() {public void run() {// 要执行的任务} }; // 启动线程 t.start(); 例如&#xff1a; // 构造方法的参数是给线程指定名字&#xff0c;推荐 Thread t1 new Thread("t1") {Override// run 方法内实现…

手把手教你PXE高效网络装机、Kickstart无人值守安装(详细版)

目录 一、部署PXE远程安装服务1.1PXE定义1.2PXE服务优点1.3搭建网络体系前提条件1.4 搭建PXE远程安装服务器 二. 实验2.1 服务器操作2.2 安装启动TFTP服务并修改TFTP服务的配置文件2.3 安装并启用DHCP服务2.4 准备linux内核&#xff0c;初始化镜像文件2.5 准备PXE引导程序2.6 安…

22、Tweak原理及部分逆向防护

一、Tweak原理 1.1 Tweak产物.dylib 执行make命令时,在 .theos的隐藏目录中,编译出obj/debug目录,包含 arm64、arm64e两种架构,同时生成readbadges.dylib动态库 在arm64、arm64e目录下,有各自架构的readbadges.dylib,而debug目录下的readbadges.dylib,是一个胖二进制文件 fi…

【Java-01】深入浅出匿名对象 , 继承 , 抽象类

主要内容 面向对象回顾 匿名对象介绍 面向对象特征 - 继承 抽象类的使用 模板设计模式 1 面向对象回顾 面向对象的核心思想是什么 ? 用代码来模拟现实生活中的事物 , 比如学生类表示学生事物 , 对象表示的就是具体的学生 , 有了类就可以描述万千世界所有的事物了 现有的…

看完这篇文章你就彻底懂啦{保姆级讲解}-----(LeetCode刷题142环形链表II) 2023.4.24

目录 前言算法题&#xff08;LeetCode刷题142环形链表II&#xff09;—&#xff08;保姆级别讲解&#xff09;分析题目&#xff1a;算法思想环形链表II代码&#xff1a;补充 结束语 前言 本文章一部分内容参考于《代码随想录》----如有侵权请联系作者删除即可&#xff0c;撰写…