C++中的接口继承和实现继承以及多态性与性能的平衡处理

接口继承

接口继承是指子类只继承基类的纯虚函数,即只继承基类的接口,而不继承基类的实现。子类必须实现基类中的所有纯虚函数,否则子类也将成为抽象类。在 C++ 中,接口继承主要通过抽象类来实现。抽象类是包含至少一个纯虚函数的类。纯虚函数是在基类中声明但没有定义的虚函数,用 “= 0” 来标记。例如:

下面定义了一个抽象类Shape,它就像是一个接口,规定了派生类必须实现area和draw函数


class Shape {
public:virtual double area() = 0;virtual void draw() = 0;
};

当一个类继承自抽象类时,它必须实现抽象类中的所有纯虚函数,否则这个派生类也会成为抽象类。例如,定义一个Circle类继承自Shape

 class Circle : public Shape {private:double radius;public:Circle(double r) : radius(r) {}double area() override {return 3.14159 * radius * radius;}void draw() override {drawCircle(radius);}};

接口继承的重点在于定义规范,强制派生类提供符合接口要求的实现,从而使不同的派生类能够以统一的方式被使用,这有利于实现多态性。

实现继承

实现继承是指子类不仅继承基类的接口,还继承基类的实现。子类可以选择重写基类的函数,也可以直接使用基类的函数实现。实现继承是指派生类继承基类的成员函数和成员变量的实现。在 C++ 中,通过普通的类继承机制实现。例如,有一个基类Vehicle

class Vehicle {
protected:int speed;
public:Vehicle(int s) : speed(s) {}void move() {cout << "Vehicle is moving at " << speed << " mph." << endl;}
};

然后定义一个派生类Car继承自Vehicle

  class Car : public Vehicle {public:Car(int s) : Vehicle(s) {}void move() {cout << "Car is moving at " << speed << " mph." << endl;}};

这里Car类继承了Vehicle类的speed成员变量和move函数的实现。Car类可以选择重写move函数,也可以不重写而直接使用Vehicle类中的move函数实现。

实现继承侧重于代码复用。派生类可以继承基类已经实现好的功能,减少代码的重复编写。同时,派生类还可以根据需要对继承的函数进行重写,以实现特定的行为。在上述例子中,Car类继承了Vehicle类的基本移动功能move,并在自己的move函数实现中复用了基类中的speed成员变量。

总结接口继承和实现继承的区别

1.继承内容:接口继承主要继承的是纯虚函数的接口规范,没有函数体的继承;实现继承则继承了基类的成员变量和成员函数的实际实现。

2.实现要求:接口继承要求派生类必须实现所有纯虚函数;实现继承中派生类可以选择重写或者不重写基类的函数。

3.设计目的:接口继承用于定义统一的行为规范,方便实现多态;实现继承用于代码复用和基于已有功能的扩展。

接口继承与实现继承在面向对象编程中的应用场景

接口继承:适用于定义一组通用的接口,让不同的类实现这些接口,以实现多态性。例如,在图形绘制系统中,可以定义一个抽象的图形接口类,让不同的具体图形类实现这个接口。

实现继承:适用于在已有类的基础上进行扩展和修改,继承基类的实现可以减少代码重复。例如,在一个游戏开发中,可能有一个基础的角色类,其他具体的角色类可以继承这个基础角色类的实现,并进行特定的扩展。

如何处理多态性与性能的平衡

多态性对性能的影响:

多态性通常通过虚函数实现,而虚函数的调用会带来一定的性能开销。这是因为在运行时确定要调用的具体函数需要通过虚函数表进行查找,相比直接调用非虚函数会稍微慢一些。此外如果大量使用多态性,可能会增加程序的内存占用,因为每个包含虚函数的类都需要维护一个虚函数表。

1.虚函数调用机制

C++ 中的多态性通过虚函数实现。当通过基类指针或引用调用虚函数时,程序需要在运行时查找虚函数表来确定要调用的实际函数。这个查找过程会产生一定的性能开销。例如:

class Base {
public:virtual void func() {cout << "Base::func" << endl;}
};
class Derived : public Base {
public:void func() override {cout << "Derived::func" << endl;}
};
int main(){Base* ptr = new Derived();ptr->func();delete ptr;return 0;
}

在这个例子中,当通过ptr(Base类指针)调用func函数时,编译器会在运行时根据ptr所指向对象的实际类型(Derived)从虚函数表中查找func函数的地址,这相比于直接调用非虚函数会更耗时。

2.内存开销

每个包含虚函数的类都需要维护一个虚函数表,这会增加内存占用。在大型程序中,如果有大量包含虚函数的类和对象,虚函数表所占用的内存可能会比较可观。

如何在面向对象编程中平衡多态性与性能:

1.谨慎使用多态性:只在真正需要多态行为的地方使用虚函数,避免不必要的多态调用。例如,如果一个函数在大多数情况下只需要调用基类的实现,可以将其定义为非虚函数。例如,在一个简单的图形绘制系统中,如果对于大多数基本形状(如简单的矩形、圆形),绘制函数的行为是固定的,那么可以将这个绘制函数在基类中定义为非虚函数,以避免虚函数调用的开销。

2.优化关键路径:对于性能关键的代码路径,可以考虑避免使用多态性,或者使用其他技术如模板元编程来实现类似的功能,以提高性能。模板是在编译时确定类型的,不会有运行时多态性的开销。例如,在一个数学计算库中,对于矩阵乘法这种性能关键的操作,如果矩阵类型是固定的几种(如整数矩阵、浮点数矩阵),可以使用模板来实现不同类型矩阵的乘法,而不是通过多态性。

3.考虑内联虚函数:如果虚函数的实现非常小,可以考虑将其声明为内联函数,这样可以减少函数调用的开销。但需要注意的是,内联是由编译器决定的,不一定能保证被内联。例如:对于getValue这样简单的虚函数,可以尝试将其声明为内联函数,但不能保证编译器一定会内联这个函数。

class S{public:virtual int getValue() {return 5;}};

举例说明在实际项目中如何平衡多态性与性能:

在一个游戏引擎中,对于频繁调用的游戏对象更新函数,如果性能要求非常高,可以考虑使用模板或者其他技术来代替多态性。而对于一些不太频繁调用的、需要根据不同类型的游戏对象执行不同行为的函数,可以使用多态性来实现。例如在游戏开发的角色系统中,假设有一个Character基类,有move、attack等虚函数用于实现角色的移动和攻击行为。对于频繁调用的update函数(用于更新角色状态),如果其行为在大多数角色类型中是相似的,可以将update函数定义为非虚函数,以避免虚函数调用的开销。而对于attack等函数,因为不同角色(如战士、法师)的攻击方式差异较大,需要多态性来实现不同的行为,这时可以通过合理的类设计和虚函数调用,同时注意对关键性能路径的优化,如在update函数中尽量减少虚函数调用,来平衡多态性和性能。

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

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

相关文章

大数据相关标准——GB/T 38676-2020信息技术 大数据 存储与处理系统功能测试要求(山东省大数据职称考试)

大数据分析应用-初级 第一部分 基础知识 一、大数据法律法规、政策文件、相关标准 二、计算机基础知识 三、信息化基础知识 四、密码学 五、大数据安全 六、数据库系统 七、数据仓库. 第二部分 专业知识 一、大数据技术与应用 二、大数据分析模型 三、数据科学 大数据相关标准…

Mysql基础操作(1)

目录 简介 1. 数据库的创建与删除 1.1 创建数据库 1.2 删除数据库 2. 表的创建与删除 2.1 创建表 2.2 删除表 3. 数据插入 4. 数据查询 4.1 基本查询 4.2 条件查询 4.3 多条件查询 4.4 排序查询 4.5 分页查询 5. 数据更新 6. 数据删除 7. 总结 简介 MySQL 是一…

查看服务器或系统架构(amd64、arm64...)

要确定您的服务器或系统是基于i386、armhf、amd64还是arm64架构&#xff0c;可以通过一系列命令行工具来获取相关信息。以下是具体的方法和步骤&#xff1a; 1. 使用 uname 命令 uname -m 是最常用的方法之一&#xff0c;它可以快速地显示系统的硬件架构。根据输出的不同字符…

MySQL 调优技巧|索引什么时候失效?为什么?

写在前面 优化慢SQL&#xff0c;这是在工作或者面试中都不可避免的问题。这篇文章我们就来讲讲慢SQL的优化的一些方法&#xff01; 1. 升配 最简单的一步就是升配&#xff01;&#xff01;当然在降本增效的当下&#xff0c;很难能将这种单子审批下来了&#xff01; 2. 索引…

有线通信与无线通信技术的深度剖析与比较

有线通信与无线通信技术的深度剖析与比较 摘要&#xff1a; 本文详细阐述了有线通信技术和无线通信技术的原理、特点、应用场景、技术标准以及发展趋势等方面的内容&#xff0c;并对二者进行了深入的比较分析。通过全面探讨&#xff0c;旨在使读者清晰地理解这两种通信技术的差…

ARM/Linux嵌入式面经(五五):未岚大陆

文章目录 0、项目中既有flash又有E2,为什么不只使用一个?问题回答:1、uart通信与i2c通讯的硬件区别;2、说说你理解的pid算法;问题回答3、串口转usb怎么实现的?问题回答:4、软件采集的adc数据有没有滤波;问题回答5、是否使用过boot?你觉得使用boot的注意事项是什么?问…

《封装继承与多态》封装的优势

文章目录 封装在面向对象编程中的优势1. 提高代码的可读性和可维护性2. 提高代码的安全性3. 降低代码的复杂性案例举例 封装在敏捷开发和团队合作中的优势1. 促进敏捷开发2. 促进团队合作案例举例 封装在面向对象编程中的优势 封装是面向对象编程&#xff08;Object-Oriented …

高级Python游戏开发:创建一款多人对战坦克大战

在本教程中,我们将用Python的Pygame库开发一款高级的坦克大战游戏。这款游戏支持多人对战、碰撞检测、子弹射击以及地图障碍生成,适合作为学习Python高级游戏开发的练习项目。 一、游戏功能概述 多人对战模式:玩家可以操作坦克,在同一屏幕上互相攻击。子弹射击:坦克可以发…

【芯片设计- RTL 数字逻辑设计入门 番外篇 7.1 -- 基于ATE的IC测试原理】

文章目录 ATE 测试概述Opens/Shorts测试Leakage测试AC测试转自:漫谈大千世界 漫谈大千世界 2024年10月23日 23:17 湖北 ATE 测试概述 ATE(Automatic Test Equipment)是用于检测集成电路(IC)功能完整性的自动测试设备。它在半导体产业中扮演着至关重要的角色,主要用于检…

WPF+MVVM案例实战与特效(三十九)- 深度剖析一个弧形进度条的实现

文章目录 1、使用 Path 结合 ArcSegment 绘制圆弧1、属性解读2、静态圆弧3、动态圆弧4、运行效果5、圆弧两端点的形状2、总结1、使用 Path 结合 ArcSegment 绘制圆弧 1、属性解读 Path 是 WPF 中的一个标记元素,用于绘制复杂的几何路径形状,而 ArcSegment 用于描述 Path 中…

YOLOv5-Backbone模块实现

YOLOv5-Backbone模块实现 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 电脑系统&#xff1a;Windows11 显卡型号&#xff1a;NVIDIA Quadro P620 语言环境&#xff1a;python 3.9.7 编译器&#xff1a…

软件需求规格说明书文档,系统需求规格说明书下载,软件工程需求规格案例模板参考(word原件)

1 范围 1.1 系统概述 1.2 文档概述 1.3 术语及缩略语 2 引用文档 3 需求 3.1 要求的状态和方式 3.2 系统能力需求 3.3 系统外部接口需求 3.3.1 管理接口 3.3.2 业务接口 3.4 系统内部接口需求 3.5 系统内部数据需求 3.6 适应性需求 3.7 安全性需求 3.8 保密性需求 3.9 环境需求…

青少年编程与数学 02-004 Go语言Web编程 01课题、Web应用程序

青少年编程与数学 02-004 Go语言Web编程 01课题、Web应用程序 课题摘要:一、Web应用程序二、Web服务器&#xff08;一&#xff09;什么是Web服务器&#xff08;二&#xff09;Web服务器配置1. 选择服务器软件2. 安装服务器软件3. 配置服务器4. 安全设置5. 部署网站内容6. 测试服…

Linux - MySQL迁移至一主一从

Linux - MySQL迁移至一主一从 迁移准备安装MySQL ibd文件迁移原服务器操作目标服务器操作 一主一从增量同步异常解决结尾 首先部分单独安装MySQL&#xff0c;请参考Linux - MySQL安装&#xff0c;迁移数据量比较大约400G左右且网络不通故使用文件迁移&#xff0c;需开启一段时间…

29. Three.js案例-自定义平面图形

29. Three.js案例-自定义平面图形 实现效果 知识点 WebGLRenderer WebGLRenderer 是 Three.js 中用于渲染 3D 场景的核心类。它利用 WebGL 技术在浏览器中渲染 3D 图形。 构造器 THREE.WebGLRenderer(parameters : object) 参数类型描述parametersobject可选参数对象&…

SpringBoot开发——集成Java审计日志工具Spring Data Envers

文章目录 一、Spring Data Envers是什么二、集成步骤1、添加依赖2、然后在启动类上添加启用注解3、创建一个审计实体4、使用审计仓库5、高级功能 三、总结 一、Spring Data Envers是什么 Spring Data Envers是Spring Data家族中专门负责数据审计的成员。它基于Hibernate Enver…

大模型运用-Prompt Engineering(提示工程)

什么是提示工程 提示工程 提示工程也叫指令工程&#xff0c;涉及到如何设计、优化和管理这些Prompt&#xff0c;以确保AI模型能够准确、高效地执行用户的指令&#xff0c;如&#xff1a;讲个笑话、java写个排序算法等 使用目的 1.获得具体问题的具体结果。&#xff08;如&…

MTK Android12 更换开机LOGO和开机动画

1、路径&#xff1a; &#xff08;1&#xff09;device/mediatek/system/common/device.mk &#xff08;2&#xff09;vendor/audio-logo/animation/bootanimation.zip &#xff08;3&#xff09;vendor/audio-logo/products/resource-copy.mk &#xff08;4&#xff09;vendo…

嵌入式驱动开发详解16(音频驱动开发)

文章目录 前言WM8960简介I2S协议接口说明 SAI音频接口简介驱动框架简介设备树配置内核使能声卡设置与测试 后续参考文献 前言 该专栏主要是讲解嵌入式相关的驱动开发&#xff0c;但是由于ALSA驱动框架过于复杂&#xff0c;实现音频编解码芯片的驱动不是一个人能完成的&#xf…

[146 LRU缓存](https://leetcode.cn/problems/lru-cache/)

分析 维护一个双向链表保存缓存中的元素。 如果元素超过容量阈值&#xff0c;则删除最久未使用的元素。为了实现这个功能&#xff0c;将get(), put()方法获取的元素添加到链表首部。 为了在O(1)时间复杂度执行get()方法&#xff0c;再新建一个映射表&#xff0c;缓存key与链表…