C++中实现多态有几种方式

一)虚函数(Virtual Functions)实现多态
概念:
虚函数是在基类中使用关键字virtual声明的成员函数。当一个类包含虚函数时,编译器会为该类创建一个虚函数表(v - table),这个表存储了虚函数的地址。当通过基类指针或引用调用虚函数时,程序会根据对象的实际类型(即指针或引用所指向或引用的实际子类对象)来查找虚函数表,从而调用子类中重写后的虚函数,实现多态行为。
代码示例:

class Shape 
{
public:virtual void draw() = 0; 
};
class Circle : public Shape 
{
public:void draw() override {std::cout << "Drawing a circle." << std::endl;}
};
class Rectangle : public Shape 
{
public:void draw() override {std::cout << "Drawing a rectangle." << std::endl;}
};

在这个例子中,Shape是一个抽象基类,draw是一个纯虚函数。Circle和Rectangle是Shape的子类,它们重写了draw函数。当使用
Shape* shapePtr;
shapePtr = new Circle();
shapePtr->draw();
或者
Shape circleObj;
Shape& shapeRef = circleObj;
shapeRef.draw();
(circleObj是Circle类的对象)这样的方式调用draw函数时,会根据对象是Circle还是Rectangle来动态地调用相应子类的draw函数,实现多态。
二)函数指针实现多态(较少使用,但在某些特定场景下有效)
概念:
可以通过定义函数指针,让函数指针指向不同的函数实现来达到类似多态的效果。函数指针可以根据具体的情况(如运行时的条件)来改变它所指向的函数,从而实现不同的行为。不过这种方式与虚函数相比,没有自动的动态绑定机制,需要手动管理函数指针的指向。
代码示例:

// 定义函数指针类型,该函数接受无参数,返回void
typedef void (*DrawFunction)();
class Shape 
{
public:DrawFunction drawFunction;Shape(DrawFunction df) : drawFunction(df) {}void draw() {drawFunction();}
};
void drawCircle() 
{std::cout << "Drawing a circle using function pointer." << std::endl;
}
void drawRectangle() 
{std::cout << "Drawing a rectangle using function pointer." << std::endl;
}
int main() 
{Shape circleShape(drawCircle);Shape rectangleShape(drawRectangle);circleShape.draw();rectangleShape.draw();return 0;
}

在这个例子中,Shape类中有一个DrawFunction类型的函数指针drawFunction。在构造函数中,可以传入不同的函数来初始化这个函数指针。当调用draw方法时,就会执行函数指针所指向的函数。通过这种方式,可以实现根据不同的对象(这里通过不同的构造方式)来执行不同的绘制函数,达到类似多态的效果。不过这种方式需要手动设置函数指针,而且对于继承体系的支持不如虚函数方便。
代码示例:

#include <iostream>
// 定义函数指针类型,该函数接受两个整数参数并返回一个整数
typedef int (*MathOperation)(int, int);
int add(int a, int b) 
{return a + b;
}
int subtract(int a, int b) 
{return a - b;
}
int main()
{MathOperation operation;operation = add;int result1 = operation(5, 3);operation = subtract;int result2 = operation(5, 3);std::cout << "加法结果: " << result1 << std::endl;std::cout << "减法结果: " << result2 << std::endl;return 0;
}

在这个例子中,MathOperation是一个函数指针类型。通过将operation函数指针先后指向add函数和subtract函数,实现了根据需要调用不同函数的效果,从而在一定程度上实现了多态。
三)模板(Templates)实现编译时多态(也称为参数化多态)
概念:
模板是 C++ 中的泛型编程机制。虽然它不是真正的多态(因为它是在编译时确定具体的函数或类的版本,而不是运行时),但在某些情况下可以实现类似多态的代码复用和灵活性。通过模板,可以编写通用的代码,这些代码可以根据不同的类型参数生成不同的具体实现,从而适应多种数据类型或类类型的需求。
代码示例(函数模板):

template<typename T>
void draw(T& shape) 
{shape.draw();
}
class Circle 
{
public:void draw() {std::cout << "Drawing a circle using template." << std::endl;}
};
class Rectangle 
{
public:void draw() {std::cout << "Drawing a rectangle using template." << std::endl;}
};
int main() 
{Circle circle;Rectangle rectangle;draw(circle);draw(rectangle);return 0;
}

在这个例子中,draw是一个函数模板,它可以接受不同类型的参数(只要这个类型有draw方法)。当传入Circle或Rectangle对象时,会在编译时根据对象的类型生成对应的draw函数调用,实现了对不同类型对象的通用处理,有点类似于多态的效果,但这种方式是基于编译时的类型推导,而不是像虚函数那样的运行时多态。
四)抽象基类与纯虚函数结合实现多态(与虚函数方式紧密相关)
概念:
抽象基类是包含纯虚函数的类,不能被实例化。纯虚函数是在基类中声明但没有定义的虚函数,它强制子类必须重写这个函数。通过这种方式,可以定义一个通用的接口,子类必须实现这个接口,从而实现多态。当通过基类指针或引用调用这些纯虚函数时,会根据子类的实际实现来调用相应的函数。
代码示例(与前面虚函数示例结合):
class Shape
{
public:
virtual void draw() = 0;
};
这里Shape是抽象基类,draw是纯虚函数。子类必须重写draw函数才能实例化,这样就确保了在通过Shape基类指针或引用调用draw函数时,能够根据具体子类的实现来获得不同的绘制行为,实现多态。这种方式明确了接口规范,并且和虚函数的动态绑定机制一起,是 C++ 实现多态的重要方式之一
五)函数重载(Function Overloading)实现有限的多态性
概念:
函数重载是指在同一个作用域内,可以有多个同名函数,它们的参数列表(参数个数、类型、顺序)不同。当调用一个重载函数时,编译器会根据传入的实际参数来确定调用哪一个具体的函数版本。这种方式在一定程度上实现了多态性,因为相同的函数名可以根据不同的参数类型执行不同的操作。
代码示例:

#include <iostream>
class Calculator 
{
public:int add(int a, int b){return a + b;}double add(double a, double b) {return a + b;}
};
int main() 
{Calculator calculator;int intResult = calculator.add(3, 5);double doubleResult = calculator.add(3.5, 2.5);std::cout << "整数相加结果: " << intResult << std::endl;std::cout << "浮点数相加结果: " << doubleResult << std::endl;return 0;
}

在这个例子中,Calculator类中有两个add函数,一个用于整数相加,一个用于浮点数相加。编译器会根据传入add函数的参数类型来决定调用哪个版本,这就像一种简单的多态,根据参数类型的不同选择不同的操作方式。不过,函数重载是在编译时确定调用的函数版本,而不是像虚函数那样在运行时动态绑定
六)函数对象(仿函数,Functors):
原理:函数对象是一个类,它重载了函数调用运算符operator()。这样,这个类的对象就可以像函数一样被调用。例如:

class AddFunctor 
{
public:int operator()(int a, int b) const {return a + b;}
};
class SubtractFunctor 
{
public:int operator()(int a, int b) const {return a - b;}
};
int main() 
{AddFunctor addObj;SubtractFunctor subtractObj;int result1 = addObj(3, 2);int result2 = subtractObj(3, 2);return 0;
}

应用场景:在 STL 算法中广泛使用。例如,std::sort函数可以接受一个比较函数对象来确定排序的顺序。不同的比较函数对象可以实现不同的排序规则(如升序、降序),通过这种方式实现了基于不同规则的排序多态性。同时,函数对象可以携带状态(通过类的成员变量),这是函数指针所不具备的优势,在一些需要记录中间状态的场景中非常有用。

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

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

相关文章

汽车IVI中控开发入门及进阶(三十七):基于HFP协议的蓝牙电话

概述: HFP全称Hands-free Profile,是一款让蓝牙设备控制电话的软件,多用于汽车上。此类设备最常见的例子是车载免提装置与蜂窝电话或可穿戴无线耳机一起使用。该配置文件定义了支持免提配置文件的两个设备如何在点对点的基础上相互交互。免提模式的实现通常使耳机或嵌入式免…

线程条件变量 生产者消费者模型 Linux环境 C语言实现

只能用来解决同步问题&#xff0c;且不能独立使用&#xff0c;必须配合互斥锁一起用 头文件&#xff1a;#include <pthread.h> 类型&#xff1a;pthread_cond_t PTHREAD_COND_INITIALIZER 初始化 初始化&#xff1a;int pthread_cond_init(pthread_cond_t * cond, NULL);…

AI技术在电商行业中的应用与发展

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

高通---Camera调试流程及常见问题分析

文章目录 一、概述二、Camera配置的整体流程三、Camera的代码架构图四、Camera数据流的传递五、camera debug FAQ 一、概述 在调试camera过程中&#xff0c;经常会遇到各种状况&#xff0c;本篇文章对camera调试的流程进行梳理。对常见问题的提供一些解题思路。 二、Camera配…

高危端口汇总(Summary of High-Risk Ports)

高危端口汇总 能关闭就关闭 &#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解…

贪心算法实例-问题分析(C++)

贪心算法实例-问题分析 饼干分配问题 有一群孩子和一堆饼干&#xff0c;每个小孩都有一个饥饿度&#xff0c;每个饼干都有一个能量值&#xff0c;当饼干的能量值大于等于小孩的饥饿度时&#xff0c;小孩可以吃饱&#xff0c;求解最多有多少个孩子可以吃饱?(注:每个小孩只能吃…

图像处理网络中的模型水印

论文信息&#xff1a;Jie Zhang、Han Fang、Weiming Zhang、Wenbo Zhou、Hao Cui、Hao Cui、Nenghai Yu&#xff1a;Model Watermarking for Image Processing Networks 本文首次提出了图像处理网络中深度水印问题&#xff0c;将知识产权问题引入图像处理模型 提出了第一个深…

【后端面试总结】缓存策略选择

一般来说我们常见的缓存策略有三种&#xff0c;他们各自的优劣势和实现逻辑分别如下 Cache Aside&#xff08;旁路缓存&#xff09; 特点&#xff1a; 灵活性高&#xff1a;应用程序直接与缓存和数据库交互&#xff0c;具有高度的灵活性&#xff0c;可以根据业务需求自定义缓…

【网络安全】网站常见安全漏洞 - 网站基本组成及漏洞定义

文章目录 引言1. 一个网站的基本构成2. 一些我们经常听到的安全事件3. 网站攻击者及其意图3.1 网站攻击者的类型3.2 攻击者的意图 4. 漏洞的分类4.1 按来源分类4.2 按危害分类4.3 常见漏洞与OWASP Top 10 引言 在当今的数字化时代&#xff0c;安全问题已成为技术领域不可忽视的…

spaCy 入门与实战:强大的自然语言处理库

spaCy 入门与实战&#xff1a;强大的自然语言处理库 spaCy 是一个现代化、工业级的自然语言处理&#xff08;NLP&#xff09;库&#xff0c;以高效、易用和功能丰富著称。它被广泛应用于文本处理、信息提取和机器学习任务中。本文将介绍 spaCy 的核心功能&#xff0c;并通过一…

Ubuntu22.04系统源码编译OpenCV 4.10.0(包含opencv_contrib)

因项目需要使用不同版本的OpenCV&#xff0c;而本地的Ubuntu22.04系统装了ROS2自带OpenCV 4.5.4的版本&#xff0c;于是编译一个OpenCV 4.10.0&#xff08;带opencv_contrib&#xff09;版本&#xff0c;给特定的项目使用&#xff0c;这就不用换个设备后重新安装OpenCV 了&…

代码随想录第36天

01背包问题 二维 def hanshu():wupin, bagweight [int(x) for x in input().split()]weight [int(x) for x in input().split()]value [int(x) for x in input().split()]dp [[0]*(bagweight1) for i in range(wupin)] #dp[i][j]代表从物品【0,i-1】让任意取&#xff0c…

[C#]使用 .NET 8/9 中的 Async/Await 避免常见错误并提高性能

在.NET 8中,异步编程对于构建响应迅速且高效的应用程序至关重要。如果使用得当,async/await关键字能够简化异步代码的复杂性,但它也并非毫无挑战。在本文中,我们将探讨开发人员常犯的错误以及避免这些错误的实用策略,所有内容都将基于实际的编码场景展开。让我们深入了解如…

Dataset用load_dataset读图片和对应的caption的一个坑

代码&#xff1a; data_files {} if args.train_data_dir is not None:data_files["train"] os.path.join(args.train_data_dir, "**")dataset load_dataset("imagefolder",data_filesdata_files,cache_dirargs.cache_dir,) 数据&#xff1…

word如何快速创建目录?

文章目录 1&#xff0c;先自己写出目录的各级标题。2、选中目标标题&#xff0c;然后给它们编号3、给标题按照个人需求开始分级4、插入域构建目录。4.1、利用快捷键插入域构建目录4.2、手动插入域构建目录 听懂掌声&#xff01;学会了吗&#xff1f; 前提声明&#xff1a;我在此…

【Linux课程学习】:文件第二弹---理解一切皆文件,缓存区

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux课程学习 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 Linux学习笔记&#xff1a; https://blog.csdn.net/d…

centos 手动安装libcurl4-openssl-dev库

下载源代码 curl downloadshttps://curl.se/download/ 选择需要下载的版本&#xff0c;我下载的是8.11.0 解压 tar -zxvf curl-8.11.0 查看安装命令 查找INSTALL.md&#xff0c;一般在docs文件夹下 –prefix &#xff1a;指定安装路径&#xff08;默认安装在/usr/local&…

汽车IVI中控OS Linux driver开发实操(二十八):回声消除echo cancellation和噪声消除Noise reduction

概述: 在当今高度互联的世界中,清晰的实时通信比以往任何时候都更重要。在远程团队会议期间,没有什么能像回声一样打断对话。当说话者听到他们的声音回响时,可能会分散注意力,甚至无法理解对话。即使是很小的回声也会产生很大的影响,仅仅25毫秒的振幅就足以造成声音干扰…

javascript(前端)作为客户端端通过grpc与cpp(服务端)交互

参考文章 https://blog.csdn.net/pathfinder1987/article/details/129188540 https://blog.csdn.net/qq_45634989/article/details/128151766 前言 临时让我写前端, 一些配置不太懂, 可能文章有多余的步骤但是好歹能跑起来吧 你需要提前准备 公司有自带的这些, 但是版本大都…

客户端安全开发基础-PC篇-附项目源码

客户端安全开发基础-PC篇 written by noxke 项目源码下载 https://download.csdn.net/download/Runnymmede/90079718 1.程序分析 使用ida打开crackme.exe&#xff0c;进入到程序的主逻辑函数&#xff0c;注意到有大量的xmm寄存器&#xff0c;但是不含call指令&#xff0c;先…