C++笔记12•面向对象之继承•

继承

1.继承的概念及定义

(1)概念:
继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段,它允许程序员在 持原有类特性的基础上进行扩展 ,增加功能,这样产生新的类,称派生类。继承 呈现了面向对象 程序设计的层次结构 ,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用, 承是类设计层次的复用。
class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "peter"; // 姓名int _age = 18;  // 年龄
};
// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了
Student和Teacher复用了Person的成员。下面我们使用监视窗口查看Student和Teacher对象,可
以看到变量的复用。调用Print可以看到成员函数的复用。
class Student : public Person
{
protected:int _stuid; // 学号
};

(2)定义:

上面看到的 Person 是父类,也称作基类。 Student 是子类,也称作派生类。
(3)继承关系和访问限定符
(4)继承基类成员访问方式的变化  (范围: public > protected > private

 2.基类和派生类对象赋值转换

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

※ 基类对象不能赋值给派生类对象。
※ 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向 派生类对象 时才是安全的(否则可能存在越界访问)。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的 dynamic_cast 来进行识别后进行安全转换。(后面介绍)
class Person
{
protected :string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};
class Student : public Person
{
public :int _No ; // 学号
};
void Test ()
{Student sobj ;// 1.子类对象可以赋值给父类对象/指针/引用Person pobj = sobj ;Person* pp = &sobj;Person& rp = sobj;//2.基类对象不能赋值给派生类对象sobj = pobj;// 3.基类的指针可以通过强制类型转换赋值给派生类的指针pp = &sobjStudent* ps1 = (Student*)pp; // pp是指向派生类对象的指针(安全),这种情况转换时可以的。ps1->_No = 10;pp = &pobj;Student* ps2 = (Student*)pp; // pp是指向基类对象的指针(不安全),这种情况转换时虽然可以,但是会存在越界访问的问题ps2->_No = 10;
}

3.继承中的作用域 

(1). 在继承体系中 基类 派生类 都有 独立的作用域
(2). 子类和父类中有同名成员, 子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏, 也叫重定义。 (在子类成员函数中,可以 使用 基类 :: 基类成员 可以进行显式访问 ),
注意:隐藏的作用域是不同的,一个在父类一个在子类,要和函数重载区分,函数重载是同一个作用域。
(3). 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
(4). 注意在实际中在 继承体系里 面最好 不要定义同名的成员
代码如下:
// 示例1.Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是非常容易混淆
class Person
{
protected :string _name = "张五"; // 姓名int _num = 888;   // 身份证号
};
class Student : public Person
{
public:void Print(){cout<<" 姓名:"<<_name<< endl;cout<<" 身份证号:"<<Person::_num<< endl;cout<<" 学号:"<<_num<<endl;}
protected:int _num = 666; // 学号
};
void Test()
{Student s1;s1.Print();
};//示例2
// B中的fun和A中的fun不是构成重载,因为不是在同一作用域
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){A::fun();cout << "func(int i)->" << i << endl;}
};
void test1()
{B b;b.fun(10);//要想访问A中的fun()  要这样写 b.A::fun();
};int main()
{test1();return 0;
}

4.派生类的默认成员函数

默认成员函数讲解可以见: C++笔记3•类和对象2•-CSDN博客
(1). 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
(2). 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
(3). 派生类的 operator= 必须要调用基类的 operator= 完成基类的复制。
(4). 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
(5). 派生类对象初始化先调用基类构造再调派生类构造。
(6). 派生类对象析构清理先调用派生类析构再调基类的析构。
注意:这个地方和构造、拷贝构造不一样,不用在子类显式调用基类的析构函数,编译器会自动调用的。
(7). 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同 (后面介绍 ) 。那么编译器会对析构函数名进行特殊处理,处理成 destrutor() ,所以父类析构函数不加 virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。
代码示例:
class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};
class Student : public Person
{
public:Student(const char* name, int num): Person(name), _num(num){cout << "Student()" << endl;}Student(const Student& s): Person(s), _num(s._num){cout << "Student(const Student& s)" << endl;}Student& operator = (const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){Person::operator =(s);_num = s._num;}return *this;}~Student(){//Person::~Person();这个地方和构造、拷贝构造不一样,不用再显式调用基类(Person)的析构函数,编译器会自动调用的cout << "~Student()" << endl;}
protected:int _num; //学号
};
void test2()
{Student s1("Ann", 24);//Student s2(s1);//Student s3("Jone", 25);//s1 = s3;
}
int main()
{test2();return 0;
}

5.继承与友元

基类的友元不能被派生类继承:意思是基类友元不能访问子类私有和保护成员

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;//编译错误 基类的友元不能被派生类继承
}

6. 继承与静态成员

基类定义了 static 静态成员,则整个继承体系里面只有一个这样的成员 。无论派生出多少个子
类,都只有一个 static 成员实例。意思就是相当于全局变量。
class Person
{
public:Person() { ++_count; }
protected:string _name; // 姓名
public:static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person   //学生
{
protected:int _stuNum; // 学号
};
class Graduate : public Student   //研究生
{
protected:string _seminarCourse; // 研究科目
};
void test4()
{Student s1;Student s2;Student s3;Graduate s4;cout << " 人数 :" << Person::_count << endl;Student::_count = 0;//人数初始化cout << " 人数 :" << Person::_count << endl;
}

注意:上方出现了protected访问限定符

基类 private 成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在
派生类中能访问,就定义为 protected 可以看出保护成员限定符是因继承才出现的

7.菱形继承及菱形虚拟继承  

(1).继承的分类:单继承/多继承/菱形继承(多继承的特殊情况)

菱形继承的问题:对象成员模型构造时,可以看出菱形继承有数据冗余和二义性的问题。如上方 Assistant 的对象中 Person 成员会有两份。下图所示:
显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
class Person
{
public:string _name; // 姓名
};
class Student : public Person
{
protected:int _num; //学号
};
class Teacher : public Person
{
protected:int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
void test5()
{// 这样会有二义性无法明确知道访问的是哪一个Assistant a;//a._name = "peter";//编译器 会报出 Assistant::"name"不明确// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决a.Student::_name = "xxx";a.Teacher::_name = "yyy";
}

即解决二义性问题,又解决数据冗余问题:虚拟继承

如上面的继承关系,在 Student Teacher 的继承 Person 时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地 方去使用。代码如下:
class Person
{
public:string _name; // 姓名
};
//class Student : public Person
class Student : virtual public Person
{
protected:int _num; //学号
};
//class Teacher : public Person
class Teacher : virtual public Person
{
protected:int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};
void test6()
{Assistant a;a._name = "Jone";//可以正常访问,即解决二义性问题,又解决数据冗余问题
}

虚继承原理:

这个Person同时属于Student和Teacher,那Student和Teacher如何去找到公共的Person呢?
这里是通过了Student和Teacher的两个指针,指 向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的Person,所以就可以只有一个名字了“Jone”了。

8.继承与组合 

(1).public 继承是一种 is-a 的关系。也就是说每个派生类对象都是一个基类对象。
(比如Teacher是person,Student是person,所以Student和Teacher都可以继承person)
(2).组合是一种 has-a 的关系。假设 B 组合了 A ,每个 B 对象中都有一个 A 对象。
(比如奔驰和轮胎的关系,不能说是奔驰是轮胎,只能说是轮胎属于奔驰车的组件,所以就是组合关系)
(3).优先使用对象组合,而不是类继承 。
原因是:要有 低耦合高内聚 的思想
   1)在继承方式中,基类的 内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
   2) 组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
封装。

   3)之所以实际尽量多去用组合,是因为组合的耦合度低,代码维护性好。但是继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系既可以用继承,又可以用组合,那就优先用组合。

组合代码示例:

   // Tire和Car构成has-a的关系class Tire{protected:string _brand = "Michelin";  // 品牌size_t _size = 20;         // 尺寸};class Car{protected:string _colour = "白色"; // 颜色string _num = "*****"; // 车牌号Tire _t; // 轮胎};

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

用Leangoo领歌敏捷工具进行迭代管理的实践分享Sprint Backlog

在敏捷开发中&#xff0c;迭代管理是确保项目持续推进、不断优化的重要环节。有效的迭代管理能够帮助团队快速响应变化&#xff0c;持续交付高质量产品。 Leangoo是一款免费的敏捷项目管理工具&#xff0c;为团队提供了直观、高效的看板管理方式来管理迭代过程。本文将探讨如何…

公园智能厕所引导大屏,清楚显示厕位有无人状态

在科技飞速发展的今天&#xff0c;公园的设施也在不断与时俱进。其中&#xff0c;公园智能厕所引导大屏的出现&#xff0c;为游客带来了全新的如厕体验。 走进公园的智能厕所区域&#xff0c;首先映入眼帘的便是那醒目的引导大屏。屏幕上清晰地显示着各个厕位的有无人状态&…

星闪NearLink短距无线连接技术

星闪NearLink短距无线连接技术&#xff0c;作为华为主导的新一代无线短距通信标准技术&#xff0c;自2020年起由中国工信部牵头制定标准&#xff0c;旨在为万物互联时代提供更高效、更稳定的连接方式。 类似技术介绍 AirDrop&#xff08;苹果&#xff09; AirDrop是苹果公司开发…

【STM32+HAL库】---- 通用定时器PWM输出实现呼吸灯

硬件开发板&#xff1a;STM32G0B1RET6 软件平台&#xff1a;cubemaxkeilVScode1 新建cubemax工程 1.1 配置系统时钟RCC 1.2 配置定时器 找到LED所对应的引脚PA5&#xff0c;选择TIM2_CH1模式 在TIM2中&#xff0c;时钟源选择内部时钟Internal Clock&#xff0c;通道1选择PWM…

NanoPC-T6安装redriod笔记

这里主要用于自己对安装过程的记录&#xff0c;中间可能记录比较粗糙。 重新编译内核 参考链接&#xff1a;【环境搭建】基于linux的NanoPC-T6_LTS系统固件编译环境搭建 基于docker构建编译环境 docker run -it \ --privilegedtrue --cap-addALL \ --name nanopc_t6_lts_en…

CRM系统为贷款中介行业插上科技的翅膀

CRM&#xff08;客户关系管理&#xff09;系统为贷款中介公司插上了科技的翅膀&#xff0c;极大提升了贷款中介企业的运营效率、客户管理能力和市场竞争力。鑫鹿贷款CRM系统基于互联网、大数据分析、人工智能、云计算等前沿技术&#xff0c;帮助贷款中介公司实现业务流程的自动…

对给定数组所对应的二叉树依次完成前序,中序,后序遍历,并输出遍历结果。

对给定数组所对应的二叉树依次完成前序&#xff0c;中序&#xff0c;后序遍历&#xff0c;并输出遍历结果。每行输入为一个二叉树&#xff0c;一维数组形式。其中-1表示Nil节点&#xff0c;例如&#xff1a;1,7,2,6,-1,4,8 构成的二叉树如下图所示&#xff1a; 结果以二维数组形…

pikachu文件包含漏洞靶场

File inclusion(local) 创建1.php 步骤一&#xff1a;选择一个球员提交 ../../../../1.php File Inclusion(remote)&#xff08;远程文件包含&#xff09; 步骤一&#xff1a;更改参数 php.ini ⾥有两个重要的参数 allow_url_fopen 、allow_url_include &#xff1b; 步骤二…

springboot集成guava布隆过滤器

1.创建springboot项目&#xff0c;引入maven依赖 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.0</version></dependency>2.创建guava布隆过滤器 Component public class Gua…

浅析WebRTC技术在智慧园区视频管理场景中的应用

随着科技的飞速发展&#xff0c;智慧园区作为城市智慧化的重要组成部分&#xff0c;正逐步成为现代化管理的重要方向。智慧园区的建设不仅涉及硬件设施的智能化升级&#xff0c;还离不开高效的视频管理和实时通信技术。在这一背景下&#xff0c;WebRTC&#xff08;Web Real-Tim…

Ubuntu系统+宝塔面板部署Frp内网穿透服务

一、搭建目的 上次在局域网中搭建了自己的个人网盘之后&#xff0c;上传文件、照片都很方便&#xff0c;但是只能限制在内网中访问&#xff01;所以这次再搭建一个内网穿透服务器&#xff0c;这样不管在哪里都能访问到家里的云盘&#xff01; 二、内网穿透Frp是什么&#xff1…

猴子排序:一种理论上的排序算法

猴子排序&#xff1a;一种理论上的排序算法 在编程和算法的世界里&#xff0c;总有一些有趣的算法让人忍俊不禁&#xff0c;同时又让人深思。今天&#xff0c;我们来聊聊一种特别的排序算法——猴子排序&#xff08;Bogosort&#xff09;&#xff0c;也常被戏称为瞎子排序、波…

无需前端技能:如何使用 Amis 框架简化页面开发

Amis 是一个由百度开源的前端低代码框架&#xff0c;它允许开发者通过 JSON 配置文件来快速生成各种后台管理页面。Amis 的设计理念是通过配置而非编码来实现页面的构建&#xff0c;这使得即使是不熟悉前端技术的开发者也能快速上手。Amis 提供了丰富的组件库和模板&#xff0c…

SpringFrameWork学习笔记

本笔记基于【尚硅谷新版SSM框架全套视频教程&#xff0c;Spring6SpringBoot3最新SSM企业级开发】https://www.bilibili.com/video/BV1AP411s7D7?vd_sourcea91dafe0f846ad7bd19625e392cf76d8 总结 资料获取网址&#xff1a;https://www.wolai.com/v5Kuct5ZtPeVBk4NBUGBWF 技术…

10款高级pdf编辑器安利,能够处理99%以上pdf文件编辑问题(正版)

pdf编辑器可以帮助用户快速、高效地编辑pdf格式文档。金舟PDF编辑器支持文本、图片、注释、水印等多种元素的编辑&#xff0c;可以轻松在pdf文档中插入文字、替换内容、删除图片、移动、旋转页面等操作。 ​ PDF编辑器可以修改文字吗&#xff1f;那必然是可以的&#xff0c;而…

JDK7前时间相关类(Data,SimpleDataFormat,Calender)

Data时间类 世界标准时间&#xff1a;格林尼治时间&#xff08;GMT&#xff09; 目前世界标准时间&#xff08;UTC&#xff09;已经替换为&#xff1a;原子钟 中国标准时间&#xff1a;世界标准时间8小时 总结&#xff1a; 1.如何创建日期对象&#xff1f; Data data new…

机器学习数学公式推导之降维

文章目录 降维线性降维-主成分分析 PCA损失函数 P22 (系列五) 降维1-背景 本文参考 B站UP: shuhuai008 &#x1f339;&#x1f339; 降维 我们知道&#xff0c;解决过拟合的问题除了正则化和添加数据之外&#xff0c;降维就是最好的方法。降维的思路来源于维度灾难的问题&…

【问题分析】SetupWizard退出动画卡住【Android15】

1 问题描述 从SetupWizard退出进入Launcher的过程中&#xff0c;SetupWizard的相关界面在退出的动画过程中短暂卡在了某个阶段&#xff0c;如下图所示&#xff1a; 2 问题分析 2.1 log分析 透过现象看本质&#xff0c;看log此过程中没有冻屏之类的操作&#xff0c;那么出现长…

BIO、NIO编程与直接内存、零拷贝详解

目录 一、网络通信编程基本常识 什么是 Socket&#xff1f; 短连接 长连接 什么时候用长连接&#xff0c;短连接&#xff1f; 网络编程里通用常识 二、Java 原生网络编程-BIO 原生 JDK 网络编程 BIO 原生 JDK 网络编程 NIO 什么是 NIO&#xff1f; 和BIO 的主要区别 NI…

代码随想录算法训练营_day29

题目信息 134. 加油站 题目链接: https://leetcode.cn/problems/gas-station/题目描述: 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你…