C++笔记之实现多态的所有方法

C++笔记之实现多态的所有方法

文章目录

  • C++笔记之实现多态的所有方法
    • 1.C++中多态是是什么?请用简洁准确的话描述
    • 2.虚函数实现多态
      • 2.1.虚函数(Virtual Functions)
      • 2.2.纯虚函数(Pure Virtual Functions)
      • 2.3.虚析构函数(Virtual Destructor)
      • 2.4.虚函数表(Virtual Function Table)
    • 3.函数指针(Function Pointers)
    • 4.模板(Templates)
    • 5.达到多态性的效果
      • 5.1.接口类(Interface Classes)
      • 5.2.可调用对象(Callable Objects)
    • 6.C++中函数重载是不是一种实现多态的方法?
    • 7.虚函数表
    • 8.虚函数指针
    • 9.抄自别的文章——C++中虚函数的实现
      • 虚函数表(vtable)
      • 虚函数指针(vptr)
      • 多态的工作机制
      • 注意事项

1.C++中多态是是什么?请用简洁准确的话描述

在这里插入图片描述

2.虚函数实现多态

在C++中,实现多态性(polymorphism)的主要方法是通过使用虚函数(virtual functions)。下面是C++中实现多态性的方法:

2.1.虚函数(Virtual Functions)

在基类中声明一个虚函数,在派生类中可以对该函数进行重写。通过使用指向基类的指针或引用来调用该函数时,实际上会根据对象的实际类型调用相应的派生类函数。这是C++中最常见的多态性实现方式。

class Base {
public:virtual void polymorphicFunction() {// Base class implementation}
};class Derived : public Base {
public:void polymorphicFunction() override {// Derived class implementation}
};

2.2.纯虚函数(Pure Virtual Functions)

纯虚函数是在基类中声明的虚函数,但没有提供实际实现。派生类必须重写这些纯虚函数才能被实例化。类包含纯虚函数的类称为抽象类,抽象类不能被实例化,只能被继承。

class AbstractBase {
public:virtual void pureVirtualFunction() = 0; // Pure virtual function
};class Derived : public AbstractBase {
public:void pureVirtualFunction() override {// Derived class implementation}
};

2.3.虚析构函数(Virtual Destructor)

如果基类需要被其他类继承,而且基类中有使用了new关键字动态分配内存的成员,那么必须在基类中将析构函数声明为虚函数,以确保在删除指向派生类对象的基类指针时能正确调用派生类的析构函数,避免内存泄漏。

class Base {
public:virtual ~Base() {// Base class destructor}
};class Derived : public Base {
public:~Derived() override {// Derived class destructor}
};

2.4.虚函数表(Virtual Function Table)

C++编译器通过在包含虚函数的类中插入一个虚函数表来实现多态性。该表包含了类的虚函数地址,允许在运行时动态调用正确的函数实现。

这些是C++中实现多态性的主要方法。虚函数和纯虚函数是最常见和推荐的实现方式。

除了使用虚函数,C++中还有另外两种实现多态性的方法:

3.函数指针(Function Pointers)

使用函数指针可以在运行时实现多态性。通过定义一个指向函数的指针,然后在派生类中赋值为相应的函数,可以实现动态调用不同的函数实现。

class Base {
public:void nonPolymorphicFunction() {// Base class implementation}
};class Derived : public Base {
public:void polymorphicFunction() {// Derived class implementation}
};int main() {Base* ptr;Derived derivedObj;ptr = &derivedObj;// Function pointer for polymorphic behaviorvoid (Base::*functionPtr)() = &Base::nonPolymorphicFunction;(ptr->*functionPtr)(); // Calls Base::nonPolymorphicFunction()functionPtr = &Base::polymorphicFunction;(ptr->*functionPtr)(); // Calls Derived::polymorphicFunction()return 0;
}

4.模板(Templates)

C++的模板也可以实现多态性,特别是通过模板类和模板函数。使用模板参数,可以在编译时根据不同的参数类型生成不同的代码,从而实现多态性。

template <typename T>
class PolymorphicClass {
public:void polymorphicFunction() {// Common implementation for all types}
};template <>
void PolymorphicClass<int>::polymorphicFunction() {// Implementation specific to int
}int main() {PolymorphicClass<float> floatObj;PolymorphicClass<int> intObj;floatObj.polymorphicFunction(); // Calls the common implementationintObj.polymorphicFunction();   // Calls the specialization for intreturn 0;
}

虽然虚函数是实现多态性的最常见和推荐方法,但是这两种方法也提供了额外的灵活性,特别是在特定情况下可以使用函数指针来实现更高度定制化的多态性。模板也可以在编译时优化代码,提高性能。但这些方法相对较少使用,主要是因为虚函数在C++中提供了更为直接和方便的多态性实现。

5.达到多态性的效果

除了前面提到的虚函数、函数指针、模板和虚拟继承,C++中没有其他内置的语言特性来直接实现多态性。这些是C++中主要的方法来实现多态行为。

然而,C++是一门非常灵活的语言,允许通过各种技术和设计模式来实现更高级的多态性。下面列举了一些可能的方法,虽然它们不是直接的语言特性,但仍然可以达到多态性的效果:

5.1.接口类(Interface Classes)

C++中没有内置的接口类概念,但可以通过定义只包含纯虚函数的抽象类来模拟接口。其他类可以继承这个接口类并实现其纯虚函数,从而实现多态性。

class Interface {
public:virtual void doSomething() = 0;
};class ConcreteClass : public Interface {
public:void doSomething() override {// Implementation in ConcreteClass}
};

5.2.可调用对象(Callable Objects)

C++中的函数对象(Functor)或Lambda表达式可以作为可调用对象,使得可以在运行时动态决定调用哪个函数,从而实现多态性。

#include <functional>class Base {
public:virtual void polymorphicFunction() {// Base class implementation}
};class Derived : public Base {
public:void polymorphicFunction() override {// Derived class implementation}
};int main() {Base* ptr = new Derived;// Using std::function as a callable object for polymorphic behaviorstd::function<void(Base*)> func = [](Base* obj) { obj->polymorphicFunction(); };func(ptr); // Calls Derived::polymorphicFunction()return 0;
}

这些方法都是在C++中模拟多态性的常见手段。请注意,这些方法可能需要更多的代码和额外的管理,相比于虚函数的直接性和简洁性,可能并不是首选方案。因此,虚函数仍然是实现多态性的最常用和推荐的方式。

6.C++中函数重载是不是一种实现多态的方法?

在严格意义上,C++中的函数重载并不被认为是一种实现多态的方法。

函数重载是一种在同一个作用域内定义多个同名函数,但参数列表不同的机制。当调用这个函数时,编译器会根据传递的参数类型或数量来决定调用哪个具体的函数。函数重载主要用于提供一种方便的方式,让程序员能够使用相同的函数名处理不同类型或不同数量的数据,以增强代码的可读性和灵活性。

虽然函数重载使得代码更具灵活性和可读性,但它并没有实现多态性。多态性涉及到在运行时选择调用哪个函数的能力,这通常通过虚函数来实现,允许根据对象的实际类型来调用适当的函数。

在函数重载中,函数调用在编译时就已经确定了,不会根据对象的实际类型在运行时决定调用哪个函数。相比之下,使用虚函数时,函数调用是在运行时动态绑定的,允许在处理基类指针或引用时调用派生类中的函数实现,实现了真正的多态性。

总结来说,函数重载和多态性是不同的概念。函数重载是一种静态多态,而多态性通常指动态多态,它通过虚函数实现,允许在运行时根据对象的实际类型来选择调用适当的函数。

7.虚函数表

虚函数表(Virtual Function Table),通常简称为虚表,是C++中实现多态性的关键机制之一。它是一个用于存储虚函数地址的表格,每个具有虚函数的类都会在内存中维护一个虚函数表。

虚函数表的工作原理如下:

  1. 虚函数表是一个包含指向虚函数的指针的数组。每个虚函数在表中占据一个槽位。
  2. 对于每个包含虚函数的类,编译器会在类的布局中插入一个指向其虚函数表的指针,通常称为虚函数指针(vptr)。
  3. 当一个对象被创建时,它的虚函数指针被初始化为指向该类的虚函数表。
  4. 调用虚函数时,实际上是通过对象的虚函数指针查找虚函数表,然后调用相应的虚函数。

这个机制使得基类指针或引用可以在运行时动态地调用派生类中适当的虚函数,从而实现多态性。

以下是一个简化的示例来说明虚函数表的概念:

class Base {
public:virtual void show() {std::cout << "Base class" << std::endl;}
};class Derived : public Base {
public:void show() override {std::cout << "Derived class" << std::endl;}
};int main() {Base* basePtr;Derived derivedObj;basePtr = &derivedObj;basePtr->show(); // Calls Derived::show() through the virtual function tablereturn 0;
}

在这个示例中,Base 类和 Derived 类都有一个虚函数 show()。当通过 basePtr->show() 调用虚函数时,实际上会根据对象 derivedObj 的实际类型在虚函数表中查找正确的虚函数地址,然后调用 Derived::show()。这就是虚函数表的工作方式。

虚函数表的内部结构在不同的编译器和平台上可能会有所不同。我无法在这里直接为您提供一个完整的虚函数表的打印输出,因为虚函数表的具体结构可能会受到编译器、类的层次结构以及继承关系等因素的影响。

但是,我可以向您展示一个概念性的虚函数表结构,帮助您理解它的大致原理。请注意,这只是一个示意图,实际虚函数表的结构可能会更加复杂。

假设我们有一个类 Base 和一个派生类 Derived,它们都有一个虚函数 show()

虚函数表示意图:Base类虚函数表
+---------------------+
| 指向 Base::show()   |
+---------------------+Derived类虚函数表(继承自Base类)
+---------------------+
| 指向 Derived::show()|
+---------------------+

在这个示意图中,每个类都有一个指向其虚函数的指针,这个指针构成了虚函数表的一部分。当一个类没有虚函数时,它的虚函数表可能为空。派生类会继承基类的虚函数表,并在需要时进行覆盖。这样,通过对象的虚函数指针,可以在运行时查找到正确的虚函数实现。

请注意,虚函数表的实际结构会受到编译器优化和内存布局等因素的影响,因此它的具体形式可能会因编译器而异。

8.虚函数指针

虚函数指针的具体样子会受到编译器和平台的影响,因此无法直接为您提供精确的打印输出。虚函数指针通常是一个指向虚函数表的指针,用于在运行时查找和调用适当的虚函数。

以下是一个简化的示例来说明虚函数指针的概念:

#include <iostream>class Base {
public:virtual void show() {std::cout << "Base class" << std::endl;}
};class Derived : public Base {
public:void show() override {std::cout << "Derived class" << std::endl;}
};int main() {Base* basePtr;Derived derivedObj;basePtr = &derivedObj;// 虚函数指针的示意,这是一个虚函数表的指针void** vptr = reinterpret_cast<void**>(basePtr);// 第一个槽位通常是指向虚函数表本身的指针,后面是实际的虚函数指针// 注意:实际虚函数指针的内容可能因编译器和平台而异void* firstSlot = *vptr;void* showFunctionPtr = *(vptr + 1);std::cout << "Address of virtual function table: " << firstSlot << std::endl;std::cout << "Address of show() function: " << showFunctionPtr << std::endl;return 0;
}

请注意,这个示例中展示的内容是概念性的,实际虚函数指针的结构会更加复杂,并且受到编译器和平台的影响。虚函数指针通常位于对象的内存布局的开头部分,并指向类的虚函数表。在运行时,通过虚函数指针查找虚函数表,然后找到正确的虚函数地址进行调用。

9.抄自别的文章——C++中虚函数的实现

在这里插入图片描述
C++ 中虚函数的实现涉及到虚函数表(vtable)和虚函数指针(vptr),这是实现多态性的关键。

虚函数表和虚函数指针允许程序在运行时确定要调用的函数,而不是在编译时确定。

虚函数表(vtable)

对于每个拥有虚函数的类,编译器会生成一个虚函数表,通常在类的内部,作为类的一部分。这个虚函数表是一个指向函数指针的数组,其中包含了该类中每个虚函数的地址。虚函数表的第一个函数指针通常指向类的析构函数。接下来是按照虚函数在类中声明的顺序排列的函数指针。子类将继承父类的虚函数表,并可以在其末尾添加自己的虚函数指针。

虚函数指针(vptr)

对于每个类的对象,编译器会生成一个虚函数指针(通常称为vptr),该指针指向对象所属类的虚函数表。这个vptr通常位于对象的内存布局的开头。当调用虚函数时,实际上是通过对象的vptr找到正确的虚函数表,然后在虚函数表中查找要调用的函数的地址,最后执行该函数。

多态的工作机制

当通过基类指针或引用调用虚函数时,程序首先访问对象的vptr,然后从虚函数表中找到要调用的函数。这个机制允许在运行时根据对象的实际类型而不是指针或引用的静态类型来调用正确的函数,实现了多态性。

注意事项

构造函数不能是虚函数,因为在对象的构造过程中,虚函数表可能尚未完全设置。而析构函数可以为虚函数。C++标准并未规定虚函数表和虚函数指针的实现方式,因此不同编译器可能有不同的底层实现。但在大多数情况下,它们遵循上述概念。

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

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

相关文章

vue学习part01

02_Vue简介_哔哩哔哩_bilibili Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org) 1.简介 2.常用用法 新项目一般vue3&#xff0c;老项目vue2 3.vue两种风格&#xff1a;选项式api&#xff08;vue2&#xff09;和组合式api&#xff08;vue3&#xff09; 两种方式实现累…

Scala集合操作

1 集合简介 Scala 中拥有多种集合类型&#xff0c;主要分为可变的和不可变的集合两大类&#xff1a; 可变集合&#xff1a; 可以被修改。即可以更改&#xff0c;添加&#xff0c;删除集合中的元素&#xff1b; 不可变集合类&#xff1a;不能被修改。对集合执行更改&#xff0c;…

基于单片机控制的GSM短信模块家庭防盗报警系统

博主主页&#xff1a;单片机辅导设计 博主简介&#xff1a;专注单片机技术领域和毕业设计项目。 主要内容&#xff1a;毕业设计、简历模板、学习资料、技术咨询。 文章目录 主要介绍一、内容1 设计任务和要求1 主要内容 二、系统总体方案2.1 系统整体设计思路2.2 系统方案设计 …

【入门Flink】- 03Flink部署

集群角色 Flik提交作业和执行任务&#xff0c;需要几个关键组件&#xff1a; 客户端(Client)&#xff1a;代码由客户端获取并做转换&#xff0c;之后提交给JobManger JobManager&#xff1a;就是Fink集群里的“管事人”&#xff0c;对作业进行中央调度管理&#xff1b;而它获…

【ES专题】ElasticSearch 高级查询语法Query DSL实战

目录 前言阅读对象阅读导航前置知识数据准备笔记正文一、ES高级查询Query DSL1.1 基本介绍1.2 简单查询之——match-all&#xff08;匹配所有&#xff09;1.2.1 返回源数据_source1.2.2 返回指定条数size1.2.3 分页查询from&size1.2.4 指定字段排序sort 1.3 简单查询之——…

服务上千家企业,矩阵通2.0重磅上线,全链路管理新媒体矩阵

自上线以来 矩阵通已服务了上千家企业级客户 覆盖汽车、家居、媒体、金融、教育等多个行业 矩阵通1.0时代 我们以“数据”为基座打造出10功能 帮助企业轻松管理新媒体矩阵 实现账号管理、数据分析、竞对监测、 人员考核、风险监管等需求 而现在 矩阵通2.0重磅上线 新增…

0基础学习PyFlink——个数滑动窗口(Sliding Count Windows)

大纲 滑动&#xff08;Sliding&#xff09;和滚动&#xff08;Tumbling&#xff09;的区别样例窗口为2&#xff0c;滑动距离为1窗口为3&#xff0c;滑动距离为1窗口为3&#xff0c;滑动距离为2窗口为3&#xff0c;滑动距离为3 完整代码参考资料 在 《0基础学习PyFlink——个数…

使用vscode实现远程开发,并通过内网穿透在公网环境下远程连接

文章目录 前言1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 前言 远程…

学习笔记三十四:Ingress和 Ingress Controller概述

Ingress和 Ingress Controller概述 回顾service四层负载在k8s中为什么要做负载均衡Service不足之处四层负载和七层负载的区别OSI七层模型&#xff1a; Ingress介绍Ingress Controller介绍Ingress-controller 作用Ingress和Ingress Controller总结使用Ingress Controller代理k8s…

设备接入服务组件->微服务and容器化改造说明文档

SVN路径 https://192.0.0.241/USTA-dac/branches/dev/V1.10.500/dac 目录结构 das为设备接入服务&#xff0c;负责驱动管理&#xff0c;资源同步&#xff0c;订阅下发。下面有两个文件夹分别对应了openssl1.0的版本和后面更换接口后openssl1.1的版本。das_proxy为设备信令下发…

家用NAS上的Linux虚拟机上安装Domino

大家好&#xff0c;才是真的好。 此篇不是广告&#xff0c;毕竟没有任何人给广告费。 就是我个人入手了一台NAS设备&#xff0c;一开始用途比较淳朴&#xff0c;仅仅存储和家庭有关的各种照片和视频&#xff0c;但用着用着&#xff0c;就发现了NAS设备的拓展性之强&#xff0…

kubernetes集群编排——k8s存储

configmap 字面值创建 kubectl create configmap my-config --from-literalkey1config1 --from-literalkey2config2kubectl get cmkubectl describe cm my-config 通过文件创建 kubectl create configmap my-config-2 --from-file/etc/resolv.confkubectl describe cm my-confi…

提示3D标题编辑器仍在运行怎么解决,以及3D标题编辑器怎么使用

在进行视频剪辑时&#xff0c;尤其是剪辑一些带有文字的开场视频&#xff0c;一般都会使用具有立体效果的3D标题&#xff0c;这样制作出来的视频效果不仅好看&#xff0c;还非常的炫酷&#xff0c;但是对于一些刚刚开始接触视频剪辑的小伙伴来说&#xff0c;可能对3D标题还不是…

【李群李代数】【manif 】基于固定信标的2D机器人定位 (Error State Kalman Filter)...

demo演示 运行结果 我们考虑一个机器人在平面上被少量的准时地标或_信标 包围。 机器人以轴向速度和角速度的形式接收控制动作&#xff0c;并且能够测量信标相对于其自身参考系的位置。 机器人位姿 X 在 SE(2) 中&#xff0c;信标位置 b_k 在 R^2 中&#xff0c; | cos th -si…

Android系统Launcher启动流程学习(二)launcher启动

Zygote&#xff08;孵化器&#xff09;进程启动 在init进程中有解析.rc文件&#xff0c;在这个rc文件中配置了一个重要的服务service–zygote&#xff0c;这是app程序的鼻祖 zygote进程主要负责创建Java虚拟机&#xff0c;加载系统资源&#xff0c;启动SystemServer进程&#…

Postgresql批量按照顺序更新某一个字段

如批量更新采购订单行sequence字段&#xff0c;按照订单行id的顺序赋值1&#xff0c;2&#xff0c;3&#xff0c;4...&#xff1a; UPDATE purchase_order_line_copy1 SET sequence subquery.new_sequence FROM (SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS new_sequence…

Pytest-Allure及Allure命令使用

一、Allure介绍 Allure是Pytest用于生成测试报告的框架&#xff0c;提供丰富的测试报告功能&#xff1b; 二、Allure安装 Allure安装分为2块&#xff0c;分别是pytest-Allure库安装&#xff0c;本地生成报告并导出的命令行allure安装&#xff1b; 1、pytest-Allure库安装 …

时序预测 | Python实现ARIMA-CNN-LSTM差分自回归移动平均模型结合卷积长短期记忆神经网络时间序列预测

时序预测 | Python实现ARIMA-CNN-LSTM差分自回归移动平均模型结合卷积长短期记忆神经网络时间序列预测 目录 时序预测 | Python实现ARIMA-CNN-LSTM差分自回归移动平均模型结合卷积长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 时序预测 …

【vtk学习笔记4】基本数据类型

一、可视化数据的基本特点 可视化数据有以下特点&#xff1a; 离散型 计算机处理的数据是对无限、连续的空间进行采样&#xff0c;生成的有限采样点数据。在某些离散点上有精确的值&#xff0c;但点与点之间值不可知&#xff0c;只有通过插值方式获取数据具有规则或不规则的结…