C++类和对象-继承多态
继承
继承是面向对象三大特性之一
定义类时,下级别的成员除了拥有上一级的共性,还有自己的特性,就可以考虑使用继承的技术,减少代码的重复
继承的基本语法
语法:class 子类 : 继承方式 父类
- 子类也被成为派生类
- 父类也被称为基类
class A
{
public:string name;
};
class B :public A
{
public:int age;
};
int main()
{B b;b.name = "张三";b.age = 10;cout << b.name << b.age << endl;return 0;
}
继承方式
继承方式一共有三种:
- 公共继承
- 访问权限不变
- 保护继承
- 除私有内容外,都变为保护权限
- 私有继承
- 除私有内容外,都变为私有权限
父类中的私有内容,三种继承方法都无法访问
class A
{
public:int a;
protected:int b;
private:int c;
};
class B :public A//公共继承
{};
class C :protected A//保护继承
{};
class D :private A//私有继承
{};
继承中的对象模型
父类中所有非静态成员属性都会被子类继承下去
父类中私有的成员属性,是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了
利用开发人员命令提示工具查看对象模型:
- 跳转盘符:
盘符: - 跳转文件路径:
cd 具体路径下 - 查看命名:
dir - 报告单个类的布局:
cl /d1 reportSingleClassLayout类名 文件名
文件名可按Tap建自动补齐
class A
{
public:int a;
protected:int b;
private:int c;
};
class B :public A//公共继承
{int c;
};
class B size(16):+---0 | +--- (base class A)0 | | a4 | | b8 | | c| +---
12 | c+---
继承中构造和析构顺序
先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
继承同名成员处理方式
子类对象可以直接访问到子类中的同名成员
子类对象加作用域可以访问到父类同名成员
当子类与父类拥有同名的成员函数,子类会隐藏父类中所有同名成员函数,加作用域可以访问到父类中同名函数
class A
{
public:void test(){cout << "A" << endl;}
};
class B :public A//公共继承
{
public:void test(){cout << "B" << endl;}
};
int main()
{B b;b.test();b.A::test();return 0;
}
继承同名静态成员处理方式
静态成员跟非静态成员出现同名,处理方法一致,只不过有两种处理方法:
- 通过对象
. - 通过类名
::
class A
{
public:static string a;
};
class B :public A//公共继承
{
public:static string a;
};
//类内声明,类外初始化
string B::a = "B";
string B::A::a = "A";
int main()
{//通过对象访问B b;cout << b.a << endl;cout << b.A::a << endl;//通过类名访问cout << B::a << endl;//第一个::表示通过类名方式访问,第二个::代表访问父类作用域下cout << B::A::a << endl;return 0;
}
多继承语法
C++允许一个类继承多个类
语法:class 子类 : 继承方式 父类1 , 继承方式 父类2...
多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议用多继承
class A
{
public:int a;
};
class B
{
public:int a;
};
class C :public A, public B
{
};
int main()
{C c;c.A::a = 10;c.B::a = 20;cout << c.A::a << endl;cout << c.B::a << endl;return 0;
}
菱形继承
菱形继承概念:
- 两个派生类继承同一个基类
- 又有某个类同时继承这两个派生类
- 这种继承被称为菱形继承,也被称为钻石继承
典型的菱形继承问题:

菱形继承问题:子类继承两份相同的数据,导致资源浪费以及毫无意义
- 羊继承了动物的数据,驼也继承了动物的数据,当羊驼使用数据时,就会产生二义性
- 羊驼继承自动物的数据继承了两份,只需要一份就可以
利用虚继承,解决菱形继承的问题:
- 继承之前,加上关键字
virtual变为虚继承 - 公共的父类被称为虚基类
class A
{
public:int a;
};
//A为虚基类
class B :virtual public A{};
class C :virtual public A{};
class D:public B,public C{};
int main()
{D d;d.a = 10;cout << d.a << endl;return 0;
}
vbptr虚基类指针:
- v-virtual
- b-base
- ptr-pointer
虚基类指针指向vbtable虚基类表
- 实际继承了两个指针,通过偏移量,找到那份唯一的数据
class D size(24):+---0 | +--- (base class B)0 | | {vbptr}| | <alignment member> (size=4)| +---8 | +--- (base class C)8 | | {vbptr}| | <alignment member> (size=4)| +---| <alignment member> (size=4)+---+--- (virtual base A)
16 | a+---D::$vbtable@B@:0 | 01 | 16 (Dd(B+0)A)D::$vbtable@C@:0 | 01 | 8 (Dd(C+0)A)
vbi: class offset o.vbptr o.vbte fVtorDispA 16 0 4 0
多态
多态的基本语法
多态分为两类:
- 静态多态:函数重载和运算符重载属于静态多态,复用函数名
- 动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别:
- 静态多态的函数地址早绑定:编译阶段确定函数地址
- 动态多态的函数地址晚绑定:运行阶段确定函数地址
动态多态满足条件:
- 有继承关系
- 子类要重写父类的虚函数
- 使用父类的指针或引用,执行子类对象
重写不同于函数重载:
- 函数返回值类型、函数名、参数列表完全相同
C++中父子之间的类型转换不需要做强制类型转换,父类的指针或引用可以直接指向子类对象
class A
{
public://虚函数virtual void test(){cout << "A" << endl;}
};
class B :public A
{
public:void test(){cout << "B" << endl;}
};
void test(A& a)
{a.test();
}
int main()
{B b;test(b);//A & a = b;return 0;
}
多态的原理剖析
class A size(8):+---0 | {vfptr}+---A::$vftable@:| &A_meta| 00 | &A::test
class B size(8):+---0 | +--- (base class A)0 | | {vfptr}| +---+---B::$vftable@:| &B_meta| 00 | &B::test
vfptr虚函数(表)指针:
- v-virtual
- f-function
- ptr-pointer
- 指向vftable虚函数表
- 表内记录虚函数地址
&A::test
- 表内记录虚函数地址
当子类重写父类虚函数,子类中的虚函数表,内部会替换成子类的虚函数地址
多态案例-计算器类
案例描述:分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类。
多态的优点:
- 代码组织结构清晰
- 可读性强
- 利于前期和后期的扩展和维护
在真实开发中,提倡开闭原则:
- 对拓展进行开放
- 对修改进行关闭
#include <iostream>
using namespace std;
//实现计算器抽象类
class Abstract
{
public:virtual int result(){return 0;}int m_num1;int m_num2;
};
//实现计算器加法类
class add :public Abstract
{
public:virtual int result(){return m_num1 + m_num2;}
};
//实现减法类
class subtraction :public Abstract
{
public:virtual int result(){return m_num1 - m_num2;}
};
int main()
{//父类指针指向子类对象Abstract* a = new add;a->m_num1 = 10;a->m_num2 = 10;cout << a->result() << endl;//用完记得销毁delete a;//a不需要再定义和初始化a = new subtraction;a->m_num1 = 10;a->m_num2 = 10;cout << a->result() << endl;return 0;
}
纯虚函数和抽象类
在多态中,通常父类中的虚函数实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数语法:virtual 返回值类型 函数名(参数列表) = 0;
当类中有了纯虚函数,这个类也称为抽象类,特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
抽象类特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
#include <iostream>
using namespace std;
class AbstractDrinking
{
public:virtual void Boil() = 0;//煮水virtual void Brew() = 0;//冲泡virtual void PourInCup() = 0;//倒入杯中virtual void PutSomeThing() = 0;//添加佐料void MakeDrink()//制作饮品{Boil();Brew();PourInCup();PutSomeThing();}};
class Coffee :public AbstractDrinking
{virtual void Boil(){cout << "煮矿泉水" << endl;}virtual void Brew(){cout << "冲泡咖啡" << endl;}virtual void PourInCup(){cout << "倒入杯中" << endl;}virtual void PutSomeThing(){cout << "添加牛奶" << endl;}
};
class Tea :public AbstractDrinking
{virtual void Boil(){cout << "煮矿泉水" << endl;}virtual void Brew(){cout << "冲泡茶叶" << endl;}virtual void PourInCup(){cout << "倒入杯中" << endl;}virtual void PutSomeThing(){cout << "添加枸杞" << endl;}
};
void dowork(AbstractDrinking *a)
{a->MakeDrink();delete a;
}
int main()
{AbstractDrinking* a = new Tea;dowork(a);return 0;
}
虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方法:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
虚析构和纯虚析构区别:
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:virtual ~类名() {};
纯虚析构语法:virtual ~类名() = 0;
总结:
- 虚析构和纯虚析构是用来解决通过父类指针释放子类对象
- 如果子类中没有堆区数据,可以不写为虚析构和纯虚析构
- 拥有纯虚析构的类也属于抽象类
#include <iostream>
using namespace std;
class A
{
public:A(){m_name = new string("A");}virtual void speak(){cout << *m_name << endl;}//利用虚析构可以解决父类指针释放子类对象时不干净的问题virtual ~A(){if (m_name != NULL){cout << "A析构" << endl;delete m_name;m_name = NULL;}}string* m_name;
};
class B :public A
{
public:B(){m_name = new string("B");}virtual ~B(){if (m_name != NULL){cout << "B析构" << endl;delete m_name;m_name = NULL;}}
};
int main()
{A* a = new B;//先构造父类再构造子类a->speak();//父类指针在析构时,不会调用子类中析构函数,导致子类如果有堆区属性,会导致内存泄露delete a;return 0;
}
#include <iostream>
using namespace std;
class A
{
public:A(){m_name = new string("A");}virtual void speak(){cout << *m_name << endl;}//纯虚析构声明//有了纯虚析构后,这个类也属于抽象类,无法实例化对象virtual ~A() = 0;string* m_name;
};
class B :public A
{
public:B(){m_name = new string("B");}virtual ~B(){if (m_name != NULL){cout << "B析构" << endl;delete m_name;m_name = NULL;}}
};
//纯虚析构实现
A::~A()
{cout << "A纯虚析构" << endl;
}
int main()
{A* a = new B;//先构造父类再构造子类a->speak();//父类指针在析构时,不会调用子类中析构函数,导致子类如果有堆区属性,会导致内存泄露delete a;return 0;
}
多态案例-电脑组装
案例描述:
- 电脑主要组成部件为CPU、显卡、内存条
- 将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件
- 创建电脑类提供让电脑工作的函数,并且调用每个函数工作的接口
- 测试时组装三台不同的电脑进行工作
#include <iostream>
using namespace std;
//抽象不同零件类
//抽象CPU类
class CPU
{
public://抽象的计算函数virtual void calculate() = 0;
};
//抽象显卡类
class VideoCard
{
public://抽象的显示函数virtual void dispaly() = 0;
};
//抽象内存条类
class Memory
{
public://抽象的存储函数virtual void storage() = 0;
};
//电脑类
class Computer
{
public:Computer(CPU* cpu, VideoCard* vc, Memory* mem){m_cpu = cpu;m_vc = vc;m_mem = mem;}//提供析构函数,释放电脑的三个零件~Computer(){if (m_cpu != NULL){delete m_cpu;m_cpu = NULL;}if (m_vc != NULL){delete m_cpu;m_vc = NULL;}if (m_mem != NULL){delete m_cpu;m_mem = NULL;}}//提供工作函数void work(){//让零件工作起来,调用接口m_cpu->calculate();m_vc->dispaly();m_mem->storage();}
private:CPU* m_cpu;//CPU的零件指针VideoCard* m_vc;//显卡的零件指针Memory* m_mem;//内存条的零件指针
};
//具体厂商:Intel
class IntelCPU :public CPU
{
public:virtual void calculate(){cout << "Intel的CPU开始计算了" << endl;}
};
class IntelVideoCard :public VideoCard
{
public:virtual void dispaly(){cout << "Intel的显卡开始显示了" << endl;}
};
class IntelMemory :public Memory
{
public:virtual void storage(){cout << "Intel的内存开始存储了" << endl;}
};
//具体厂商:Lenovo
class LenovoCPU :public CPU
{
public:virtual void calculate(){cout << "Lenovo的CPU开始计算了" << endl;}
};
class LenovoVideoCard :public VideoCard
{
public:virtual void dispaly(){cout << "Lenovo的显卡开始显示了" << endl;}
};
class LenovoMemory :public Memory
{
public:virtual void storage(){cout << "Lenovo的内存开始存储了" << endl;}
};
int main()
{//电脑零件CPU* intelcpu = new IntelCPU;VideoCard* intervc = new IntelVideoCard;Memory* intermem = new IntelMemory;//创建第一台电脑Computer* computer1 = new Computer(intelcpu, intervc, intermem);computer1->work();delete computer1;//创建第二台电脑Computer* computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);computer2->work();delete computer2;//创建第三台电脑Computer* computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);computer3->work();delete computer3;return 0;
}
相关文章:
C++类和对象-继承多态
继承 继承是面向对象三大特性之一 定义类时,下级别的成员除了拥有上一级的共性,还有自己的特性,就可以考虑使用继承的技术,减少代码的重复 继承的基本语法 语法:class 子类 : 继承方式 父类 子类也被成为派生类父类…...
appium自动化测试
获取应用包名和入口activity:aapt命令 aapt目录: 安卓sdk的build-tools目录下(如果要在cmd里直接运行,要配置环境变量,否则需要在aapt所在目录下打开cmd) 示例: adt-bundle-windows-x86_64-20140702\sdk\build-too…...
打印流、转换流、数据流 、随机访问流
Java知识点总结:想看的可以从这里进入 目录5、打印流6、转换流7、数据流8、随机访问流5、打印流 实现将基本数据类型的数据格式转化为字符串输出,它们提供了一系列重载的print()和println()方法,用于多种数据类型的输出,这种流不会…...
Java的4种访问权限?
1、public: 所修饰的类、变量、方法,在内外包均具有访问权限;2、protected: 这种权限是为继承而设计的,protected所修饰的成员,对所有子类是可访问的,但只对同包的类是可访问的,对外…...
APP任务模块功能借助php-resque实现业务解耦
先上设计图 说明:任务模块分一次性任务和每日任务,可能还包括男女用户任务区分 处理步骤: 一、同步任务数据库 1.1、任务列表数据库 1.2、完成任务数据库 二、搭建即时消息队列 一、composer require resque/php-resque二、因为服务器red…...
怎么做,才能在职场中晋升?
1 主动原则:主动做事 工作要积极主动,刚进入职场的同学,以为“服从命令听指挥”“领导指哪打哪”就是积极主动,结果易养 1.1 不好习惯 ① 认为主管肯定会帮你搞定晋升 你可能非常信任主管,认为自己只要把主管安排的…...
Vulnhub靶场----2、DC-2
文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 DC-2下载地址:https://download.vulnhub.com/dc/DC-2.zip kali:192.168.144.148 DC-2:192.168.144.150 添加hosts文件:192.168.144.150 DC-2 二、渗透流程 nmap -A -…...
Java 基础(3)—synchornized 关键字简单理解
一、synchronized 修饰同步代码块 用 synchronized 修饰代码段作为同步锁,代码如下: public class LockDemo {public Object object new Object();public void show(){synchronized (object) {System.out.println(">>>>>>hell…...
【Linux】调试工具gdb的使用
环境:centos7.6,腾讯云服务器Linux文章都放在了专栏:【Linux】欢迎支持订阅🌹前言在前文,我们已经讲解了vim工具以及gcc/g的使用,我们可以进行编写代码以及编译代码了,但是还没有学习如何在Linu…...
大数据知识图谱项目——基于知识图谱的医疗知识问答系统(详细讲解及源码)
基于知识图谱的医疗知识问答系统 一、项目概述 本项目基于医疗方面知识的问答,通过搭建一个医疗领域知识图谱,并以该知识图谱完成自动问答与分析服务。本项目以neo4j作为存储,基于传统规则的方式完成了知识问答,并最终以关键词执…...
威马汽车:跃马扬鞭未竟,鞍马劳顿难行?
“活下去,像牲口一样地活下去。” 威马汽车创始人、董事长兼CEO沈晖1月在社交媒体上分享的电影台词,已然成为威马近况的真实写照。 来源:新浪微博威马汽车沈晖Freeman 最近,网上出现了大量关于“威马汽车将实施全员停薪留职”的…...
【网络】网络基础
🥁作者: 华丞臧. 📕专栏:【网络】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文章…...
Linux系统之Uboot、Kernel、Busybox思考之三
目录 三 内核的运行 5-中断子系统 6 锁、延迟与原子上下文 7 内存管理子系统 8 驱动的两类框架 三 内核的运行 5-中断子系统 中断子系统的数据结构及设计思想。 中断子系统需要解决中断管理的问题。 如果系统中断较少的话,其管理就不用设计这样一个中断子系统这…...
FPGA 20个例程篇:20.USB2.0/RS232/LAN控制并行DAC输出任意频率正弦波、梯形波、三角波、方波(一)
在最后一个例程中笔者精挑细选了一个较为综合性的项目实战,其中覆盖了很多知识点,也是从一个转产产品中所提炼出来的,所以非常贴近实战项目。 整个工程实现了用户通过对上位机PC端人机界面的操作,即可达到控制豌豆开发并行DAC输出…...
性能测试学习和性能瓶颈分析路线
很多企业招聘都只写性能测试,会使用LR,jmeter工具。其实会使用jmeter和LR进行性能测试还只是性能测试的第一步,离真正的性能测试工程师还很远,笔者也还在路上 .。 性能测试,都是要求测试系统性能,系统自然…...
达梦数据库(DM8)集成使用 Geoserver(2.22.2) 以及其他对应版本详解
达梦数据库(DM8)集成使用 Geoserver(2.22.2) 以及其他对应版本详解系统环境版本Geoserver 驱动对应版本达梦 8 集成 Geoserver 过程试错过程问题总结项目需要国产化,选择使用达梦数据库,在技术测试阶段&…...
全开源无加密的RuleApp文章社区APP客户端源码
内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 开源无加密的文章社区客户端源码分享 RuleApp文章社区,VIP会员,写作投稿积分商城,付费模块集成,多平台兼容这是一款开源免费,界…...
基于springboot校园二手市场平台
一、项目简介 本项目是一套基于springboot校园二手市场平台,主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含:项目源码、数据库脚本等,该项目可以直接作为bishe使用。 项目都经过严格调试,确保…...
维度建模基本流程总结
一、维度建模基本流程图数据RD进行业务调研和数据现状调研,产出符合相关模版规范的业务知识文档和数据现状文档。数据PM也会调研相关业务产出需求设计文档,三方参与需求评审,评审通过后基建数据RD进行需求拆解,产出技术方案&#…...
RocketMQ事务消息
RocketMQ事务消息 RocketMq提供的一种高级消息类型,支持在分布式场景下面保障消息生产和本地事务的一致性 生产者将消息发送到服务端服务端将消息持久化成功后,向生产者返回ACK确认消息发送成功,此时消息状态为待投递,这种状态下的消息称之为…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
Cursor AI 账号纯净度维护与高效注册指南
Cursor AI 账号纯净度维护与高效注册指南:解决限制问题的实战方案 风车无限免费邮箱系统网页端使用说明|快速获取邮箱|cursor|windsurf|augment 问题背景 在成功解决 Cursor 环境配置问题后,许多开发者仍面临账号纯净度不足导致的限制问题。无论使用 16…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...
