C++篇之继承

1,继承的概念及定义

1.1,继承的概念

继承机制是面向对象程序设计使代码可以复用的重要手段,它允许我们在原有类的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称为派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的是函数层次的复用,这里继承是类层次的复用

下面我们看看两个类,学生类student和老师类teacher,分别放着学生和老师的一些属性和方法。

class student
{
public:
    void identity()
    {
        //...
    }
    void study()
    {
        cout << " void study()" << endl;
    }
protected:
    string _name = "张三";
    string adderss;   //地址
    string _tel;      //电话
    int _age = 18;    //年龄
    int _id;          //学号
};
class teacher
{
public:
    void identity()
    {
        //...
    }
    void teaching()
    {
        cout << "teaching" << endl;
    }
protected:
    string _name = "张三";
    string adderss;   //地址
    string _tel;      //电话
    int _age = 18;    //年龄
     
    string _title;    //职称
}; 

从上面的代码可以看出,Student和Teacher都有姓名/地址/ 电话/年龄等成员变量,都有identity⾝份认证的成员函数,设计到两个类⾥⾯就是冗余的。 当然他们 也有⼀些不同的成员变量和函数,⽐如⽼师独有成员变量是职称,学⽣的独有成员变量是学号;学⽣ 的独有成员函数是学习,⽼师的独有成员函数是授课。

下⾯我们公共的成员都放到Person类中,Student和teacher都继承Person,就可以复⽤这些成员,就 不需要重复定义了,省去了很多⿇烦 

class person
{
public:
    void identity()
    {
        //...
    }
protected:
    string _name = "张三";
    string adderss;   //地址
    string _tel;      //电话
    int _age = 18;    //年龄

};

class student :public person
{
public:
    void identity()
    {
        //...
    }
    void study()
    {
        cout << " void study()" << endl;
    }
protected:
    int _id;          //学号
};

class teacher: public person
{
public:
    void teaching()
    {
        cout << "teaching" << endl;
    }
protected:

    string _title;    //职称
};

1.2,继承定义 

1.2.1,定义格式

1.2.2,继承基类的成员,派生类访问方式的变化

1. 基类private成员在派⽣类中无论以什么⽅式继承都是不可⻅的。这⾥的不可见是指基类的私有成员 还是被继承到了派⽣类对象中,但是语法上限制派⽣类对象不管在类里面还是类外面都不能去访 问它。

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

3,实际上⾯的表格我们进⾏⼀下总结会发现,基类的私有成员在派⽣类都是不可见。基类的其他成员 在派⽣类的访问⽅式==Min(成员在基类的访问限定符,继承方式),public >protected> private。

4. 使⽤关键字class时默认的继承⽅式是private,使⽤struct时默认的继承⽅式是public,不过最好显示的写出继承⽅式。

5. 在实际运⽤中⼀般使⽤都是public继承,⼏乎很少使⽤protetced/private继承,也不提倡使⽤ protetced/private继承,因为protetced/private继承下来的成员都只能在派⽣类的类里面使用,

2,基类和派生类间的转化

1, public继承的派⽣类对象可以赋值给基类的指针/基类的引⽤。这⾥有个形象的说法叫切⽚或者切 。寓意把派⽣类中基类那部分切出来,基类指针或引用指向的是派⽣类中切出来的基类那部分。

2,基类对象不能赋值给派⽣类对象。

3,基类的指针或者引⽤可以通过强制类型转换赋值给派⽣类的指针或者引⽤。

class Person
{
protected:
	string _name;
	string _sex;
	int _age; 
};

class Student: public Person
{
public:
	int _No;
};

int main()
{
	Student sobj;
	//子类对象可以赋值给父类对象,父类指针,父类引用
	Person pobj = sobj;
	Person* p1 = &sobj;
	Person& p2 = sobj;
    Student* p3 = (Student*)p1;//父类指针可以通过强制类型转换赋值给派生类的指针        

	return 0;
}

 3,继承中的作用域

3.1,隐藏规则

1,在继承体系中,基类和派生类都有独立的作用域。

2,派生类和基类中有同名成员,派生类成员将屏蔽基类对同名成员的直接访问,这种情况叫做隐藏,(在派生类的成员函数中,可以使用基类:基类成员 显示访问

3,需要注意的是,只要基类和派生类的成员函数名相同,就构成隐藏


class person
{
protected:
    int _num=100;
    string _name = "张三";
};

class student:public person
{
public:
    void print()
    {
        cout << _name << endl;
        //与person中的_num构成隐藏,这里访问就是派生类的_num
        cout << _num << endl;
        //要想拿到基类的_num,指定类域即可
        cout << person::_num << endl;
    }
protected:
    int _num = 200;
};
int main()
{
    student s;
    s.print();
    return 0;
}

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

 1,派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员,如果基类没有默认的构造函数,则必须在派生类的初始化列表阶段显示调用。

2,派生类的拷贝构造函数必须调用基类的拷贝构造完成基类部分的拷贝初始化。

3,派生类的operator=必须调用基类的operator=完成基类的赋值,需要注意的是,基类的operator=和派生类的operator=会构成隐藏,所以在显示调用基类的operator=时3,需要指明基类的作用域。

4, 派⽣类的析构函数会在被调⽤完成后⾃动调⽤基类的析构函数清理基类成员。因为这样才能保证派 ⽣类对象先清理派⽣类成员再清理基类成员的顺序。


5.,派⽣类对象初始化先调⽤基类构造再调派⽣类构造。

6.,派⽣类对象析构清理先调⽤派⽣类析构再调基类的析构。

大致可以总结如下:

4.1,实现一个不能被继承的类 

C++11新增了一个关键字final,final修改基类,派生类就不能继承了。

5,继承与友元 

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

//示例:
//声明
class student;

class person
{
public:
    friend void display(const person& p, const student& s);
protected:
    string _name;
};


class student :public person
{
protected:
    int _num;
};

void display(const person& p, const student& s)
{
    cout << p._name << endl;
    cout << s._num << endl;
}

int main()
{
    person p;
    student s;
    display(p, s);
    return 0;
}
 

 

6,基类与静态成员 

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

7,多继承以及菱形继承 

单继承:⼀个派⽣类只有⼀个直接基类时称这个继承关系为单继承。

多继承:⼀个派⽣类有两个或以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型 是,先继承的基类在前⾯,后⾯继承的基类在后⾯,派⽣类成员在放到最后⾯。

菱形继承:菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下⾯的对象成员模型构造,可以 看出菱形继承有数据冗余和⼆义性的问题,在Assistant的对象中Person成员会有两份。⽀持多继承就 ⼀定会有菱形继承。

 

 

下面代码,对象a中包含了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;//主修课程
};

int main()
{
    Assistant a;
    //编译报错,对_name的访问不明确
    //a._name = "张三";

    cout << sizeof(a) << endl;

    //需要显示指定哪个基类的成员才能解决二义性的问题,但是数据冗余问题无法解决
    a.student::_name = "张三";
    a.teacher::_name = "李四";

    return 0;
}

 

通过监视窗口可以看出,对象a中包含了两份person的信息。 

 运行结果:

解决方法:虚拟继承

class person
{
public:
    string _name;//姓名
};

class student:virtual public person
{
protected:
    int _num;//学号
};
class teacher :virtual public person
{
protected:
    int _id;//职工编号
};
class Assistant :public student, public teacher
{
protected:
    string _majorcourse;//主修课程
};

int main()
{
    Assistant a;
    a._name = "张三";

    cout << sizeof(a) << endl;
    cout << a._name << endl;
    return 0;
}

 

通过监视窗口可以看出,对象a中虽然包含了三份person,但他们都是同一个。

所以虚拟继承可以解决菱形继承的数据冗余和二义性的问题。 

8,继承和组合

1,public继承是⼀种is-a的关系。也就是说每个派⽣类对象都是⼀个基类对象。

2,组合是⼀种has-a的关系。假设B组合了A,每个B对象中都有⼀个A对象。

示例:

//轮胎和车更符合has-a的关系
class Tire
{
protected:
    string _brand = "Michelin";//品牌
    size_t _size = 17;//尺寸
};

class Car
{
protected:
    string _car = "白色";
    string _num;   //车牌号


    Tire _t1;     //轮胎
    Tire _t2;     //轮胎
    Tire _t3;     //轮胎
    Tire _t4;     //轮胎
};

 

//BMW和Benz跟符合is-a的关系
class BMW :public Car
{
public:
    void Drive()
    {
        //...
    }
};

class Benz :public Car
{
public:
    void Drive()
    {
        //...
    }
};

总结:优先使⽤组合,而不是继承。实际尽量多去⽤组合,组合的耦合度低,代码维护性好。不过也不太 那么绝对,类之间的关系就适合继承(is-a)那就⽤继承,另外要实现多态,也必须要继承。类之间的 关系既适合⽤继承(is-a)也适合组合(has-a),就⽤组合。

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

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

相关文章

Go语言并发编程:轻松驾驭多线程世界(九)

Go语言并发编程&#xff1a;轻松驾驭多线程世界在这里插入图片描述 在现代编程中&#xff0c;并发 是让你的程序变得更强大、更高效的关键技能。幸运的是&#xff0c;Go语言提供了一种简单、直观的方式来处理并发任务&#xff0c;使用轻量级的 Goroutine 和 Channel&#xff0c…

STM32外设之SPI的介绍

### STM32外设之SPI的介绍 SPI&#xff08;Serial Peripheral Interface&#xff09;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;主要用于EEPROM、FLASH、实时时钟、AD转换器等外设的通信。SPI通信只需要四根线&#xff0c;节约了芯片的管脚&#x…

浅谈语言模型推理框架 vLLM 0.6.0性能优化

在此前的大模型技术实践中&#xff0c;我们介绍了加速并行框架Accelerate、DeepSpeed及Megatron-LM。得益于这些框架的助力&#xff0c;大模型的分布式训练得以化繁为简。 然而&#xff0c;企业又该如何将训练完成的模型实际应用部署&#xff0c;持续优化服务吞吐性能&#xf…

初始 html

html 文件结构 html 标签是整个 html 文件的根标签(最顶层标签) head 标签中写页面的属性. body 标签中写的是页面上显示的内容 title 标签中写的是页面的标题 <html><head><title>这是一个标题</title></head><body></body> <…

springboot校园支付系统-计算机毕业设计源码36348

目 录 摘要 Abstract 1 绪论 1.1 研究背景与意义 1.2 开发技术和开发特点 1.3论文结构与章节安排 2 校园支付系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据增加流程 2.2.2 数据修改流程 2.2.3 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.…

The First项目报告:抗 MEV 交易的CoW Protocol什么?

2023年&#xff0c;当UNIswap推出UniswapX 时&#xff0c;市场迎接它的不是赞叹&#xff0c;而是一片争议。UniswapX被指抄袭 CoWSwap 和 1inch。Curve 官方称 1inch 和 CoWSwap 早已改变游戏规则&#xff0c;UniswapX 非首创。CoWSwap 强调其 Intent Based Trading 的先驱地位…

【Linux系列】 环境配置文件合并的艺术:从`.env`到`.env.combined`

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Fastadmin框架短视频系统视频知识付费源码

简介&#xff1a; FastAdmin框架短视频系统/视频知识付费源码/附带小说系统 系统视频支持包月、单独购买、观影卷等功能 源码附带小说系统 源码需要配置高服务器和VDN加速 图片&#xff1a; 下载地址&#xff1a;云盘下载 原文地址&#xff1a;Fastadmin框架短视频系统视…

计算机体系结构之多级缓存、缓存miss及缓存hit(二)

前面章节《计算机体系结构之缓存机制原理及其应用&#xff08;一&#xff09;》讲了关于缓存机制的原理及其应用&#xff0c;其中提出了多级缓存、缓存miss以及缓存hit的疑问。故&#xff0c;本章将进行展开讲解&#xff0c; 多级缓存、缓存miss以及缓存hit存在的意义是为了保持…

后端SpringBoot学习项目-用户管理-增删改查

最终代码结构 仓库地址 Entity文件 数据库表设计 entity层实现 文件创建 ● 创建entity文件夹 ● 在entity层创建Java类&#xff0c;名字为User (关键字不可使用) 代码实现 package com.example.drhtspringboot.entity;import com.baomidou.mybatisplus.annotation.IdT…

华为入围Linux 内核CVE 检视“五人团”,openEuler要再进阶?

背景&#xff1a;内核社区接管 Linux 社区漏洞发布 往年 Linux 内核漏洞发布存在来源不固定、覆盖不全面&#xff0c;有时发布无修复补丁的 CVE 从而形成 0-day 漏洞等问题&#xff0c;给 Linux 内核安全带来了不确定性&#xff0c;为了更规范化运作&#xff0c;2024 年 2 月 1…

【C语言指南】C语言内存管理 深度解析

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C语言指南》 期待您的关注 引言 C语言是一种强大而灵活的编程语言&#xff0c;为程序员提供了对内存的直接控制能力。这种对内存…

12 Node.js API接口开发

八、API接口 8.1 json-server工具 1&#xff09;安装json-server npm i -g json-server2)示例 //students.json {"student":[{"id":1,"name":"sally","age":18,"gender":"女"},{"id":2,&…

前段时间我所在的公司收到了来自Nevicat的律师函

前段时间我所在的公司收到了来自Nevicat的律师函&#xff0c;至于原因嘛&#xff0c;大家懂的都懂。肯定是因为没有购买人家的正版软件&#xff0c;于是公司下令&#xff0c;所有人禁止继续使用Nevicat自行寻找其他sql工具&#xff0c;迫于无奈&#xff0c;在我使用了十几款主流…

【系统设计】理解带宽延迟积(BDP)、吞吐量、延时(RTT)与TCP发送窗口的关系:优化网络性能的关键

在设计和优化网络性能时&#xff0c;理解 带宽延迟积&#xff08;BDP&#xff09;、吞吐量、延时&#xff08;RTT&#xff09; 和 TCP发送窗口 之间的关系至关重要。这些概念相互影响&#xff0c;决定了网络连接的性能上限&#xff0c;尤其是在高带宽、高延迟的环境中&#xff…

微服务容器化部署实践(FontConfiguration.getVersion)

文章目录 前言一、整体步骤简介二、开始实战1.准备好微服务2.将各个微服务打包为镜像第一种第二种3. 将各个打包好的镜像,通过docker-compose容器编排,运行即可总结前言 docker容器化部署微服务: 将微服务容器化部署到 Docker 容器中是一个常见的做法,可以提高应用的可移…

如何监控员工上网行为?五大妙招轻松上手,员工上网监控全攻略!挖到宝啦!

如何监控员工上网行为&#xff1f; 员工的不当上网行为不仅有可能导致企业机密的泄露&#xff0c;还可能对工作效率造成显著影响。 因此&#xff0c;如何有效地监控员工的上网行为&#xff0c;已成为许多企业管理者关注的重点。 本文&#xff0c;将为您介绍五大妙招&#xff…

【C++ 算法进阶】算法提升十一 十二

目录标题 让字符串成为回文串的最少插入次数题目题目分析代码题目题目 字符子串 &#xff08;滑动窗口&#xff09;题目题目分析代码 最长连续子序列 &#xff08;头尾表&#xff09;题目题目分析代码 让字符串成为回文串的最少插入次数 题目 本题为为LC原题 题目如下 题目分…

Linux(CentOS)安装 MySQL

CentOS版本&#xff1a;CentOS 7 三种安装方式&#xff1a; 一、通过 yum 安装&#xff0c;最简单&#xff0c;一键安装&#xff0c;全程无忧。 二、通过 rpm 包安装&#xff0c;需具备基础概念及常规操作。 三、通过 gz 包安装&#xff0c;需具备配置相关操作。 --------…

6.1 软件测试:软件质量与测试

软件质量与测试 1、软价质量保证1.1 软件质量质量控制QC&#xff1a;QUALITY CONTROL质量保证QA:QUALITY ASSURANCE质量成本软件质量软件质量保证 1.2 软件评审1.3 软件可靠性 2、软件测试2.1 软件测试过程模型软件测试策略软件测试策略V模型回归测试软件测试策略原则软件测试策…