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.rhkb.cn/news/179686.html

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

相关文章

Linux基础环境开发工具的使用(yum,vim,gcc,g++)

Linux基础环境开发工具的使用[yum,vim,gcc,g] 一.yum1.yum的快速入门1.yum安装软件2.yum卸载软件 2.yum的生态环境1.操作系统的分化2.四个问题1.服务器是谁提供的呢?2.服务器上的软件是谁提供的呢?3.为什么要提供呢?4.yum是如何得知目标服务器的地址和下载链接呢?5.软件源 …

安全第一!速卖通测评补单稳定的系统注意事项大盘点

对新卖家而言&#xff0c;测评并非可耻之事&#xff0c;反而是无法起步、耗费自身时间才是真正的可耻。由于速卖通新店几乎无法获得任何活动的支持&#xff0c;流量也基本没有&#xff0c;因此要在90天内达成60单的业绩对于许多卖家来说都是一项挑战。因此&#xff0c;通过快速…

一天掌握python爬虫【基础篇】 涵盖 requests、beautifulsoup、selenium

大家好&#xff0c;我是python222小锋老师。前段时间卷了一套 Python3零基础7天入门实战 以及1小时掌握Python操作Mysql数据库之pymysql模块技术 近日锋哥又卷了一波课程&#xff0c;python爬虫【基础篇】 涵盖 requests、beautifulsoup、selenium&#xff0c;文字版视频版。1…

SpringBoot热部署2023最新版IDEA详细步骤

1、在pom.xml中配置依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional> </dependency>注意&#xff1a; 依赖放在标签里面 加入依赖后…

再学一点mybatis(原理分析)

文章目录 [TOC](文章目录) 一、mybatis是什么&#xff1f;1. Mybatis的特点以及优缺点 二、mybatis架构1.基本架构2.重要组件 三、原理1. SQL解析2. Mapper接口3. 动态代理4. SQL执行4.1 Executor4.2 StatementHandler4.3 ParameterHandler4.4 ResultHandler 文章内容有点长&am…

3D视觉引导工业机器人上下料,助力汽车制造业实现智能化生产

在工业制造领域&#xff0c;机器人技术一直是推动生产效率和质量提升的重要力量。近年来&#xff0c;随着3D视觉技术的快速发展&#xff0c;工业机器人在处理复杂任务方面迈出了重要的一步。特别是在汽车制造行业&#xff0c;3D视觉引导工业机器人的应用已经取得了令人瞩目的成…

享受户外的美好时光:花园吊椅的魅力

拥有舒适的花园吊椅&#xff0c;就像在家中创造了一个度假天堂。这些轻松摇摆的座位为您提供了一个完美的地方&#xff0c;既能舒适躺卧&#xff0c;又能让您在家中的花园或庭院中感受到度假的氛围。度过美好时光的吊椅&#xff0c;将成为家庭花园的一大亮点&#xff0c;为您带…

tensorflow-gpu 找不到指定模块

排除&#xff1a; 1.python编译器是64位 查询教程 2. cuda cudnn版本 均是12.2 可以向下兼容 cmd&#xff1a; nvcc -V即可 另一种方法 tensorflow官网教程 pip install tensorflow_gpu1.12.0 4.安装torch-gpu 检查所在环境 解决&#xff01;&#xff01; conda install …

基于深度学的图像修复 图像补全 计算机竞赛

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学的图像修复 图像补全 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-se…

IDEA在service面板中不显示微服务的项目

在.idea文件夹下的workspace文件中的project标签内添加如下代码段&#xff0c;&#xff0c;重启idea即可看到所有服务出现在了service面板中 <component name"RunDashboard"><option name"configurationTypes"><set><option value&q…

家用洗地机哪个牌子质量最好?家用洗地机推荐

洗地机也就是集吸尘器&#xff0c;拖地&#xff0c;洗地&#xff0c;功能于一体的家电&#xff0c;无论干湿垃圾都能清理的干干净净&#xff0c;而且还不用弯腰&#xff0c;有的只用换个头&#xff0c;就从拖地变成了吸尘器和除螨仪简直就是清洁家里卫生的打扫神器啦!那么面对市…

Mac电脑怎么运行 Office 办公软件

虽然 Office 软件也有 Mac 版本的&#xff0c;但是有蛮多小伙伴用起来还是感觉不得劲&#xff0c;毕竟接触了太久的 Windows&#xff0c;所以想要使用 Windows 版本的 Office 软件。 今天就给大家介绍一下怎么在 Mac 电脑中运行 Windows 版本的办公软件&#xff0c;在这里就需…

【代码学习】voxel 或者 pillar,稀疏张量 转 稠密张量 的代码理解,理解了很久

需要 feature 和 对应 的坐标 coords debug&#xff1a;转置&#xff0c;不然维度不匹配&#xff01; 对应的代码&#xff0c;向量化 应该 比 for循环快 def voxel_indexing(self, sparse_features, coords): # sparse_features: [N, C], coords:[N, 4]dim sparse_features.…

【数据结构】数组和字符串(十三):链式字符串的基本操作(串长统计、查找、复制、插入、删除、串拼接)

文章目录 4.3 字符串4.3.1 字符串的定义与存储4.3.2 字符串的基本操作&#xff08;链式存储&#xff09;1. 结构体2. 初始化3. 判空4. 串尾添加5. 打印6. 串长统计7. 查找8. 复制9. 插入10. 删除11. 串拼接12. 销毁13. 主函数14. 代码整合 4.3 字符串 字符串(String)是由零个或…

Docker compose容器编排

Docker compose容器编排 1、Docker compose简介 docker-compose是docker的编排工具&#xff0c;用于定义和运行一个项目&#xff0c;该项目包含多个docker容器&#xff0c;在如今的微服务时代&#xff0c;一个项目会存在多个服务&#xff0c;使用docker一个个部署操作的话就会…

计算机网络-应用层

文章目录 应用层协议原理万维网和HTTP协议万维网概述统一资源定位符HTML文档 超文本传输协议&#xff08;HTTP&#xff09;HTTP报文格式请求报文响应报文cookie 万维网缓存与代理服务器 DNS系统域名空间域名服务器和资源记录域名解析过程递归查询迭代查询 动态主机配置协议&…

硬件工程师到底可以从哪些方面提升自己?

大家好,这里是大话硬件。 最近在大话硬件群里,聊得比较多的就是讨论怎么提升自己的能力,怎么拿到更高的工资。我想,这可能并不是只在大话硬件群才有的话题,其实在每一位工作的人心里应该都在想的两个问题。 因此,这篇文章简单分享一下,作为一名硬件工程师,可以在做哪…

1220*2440mm建筑模板木工板:桥梁工程中的覆膜板选择

在桥梁工程中&#xff0c;选择合适的建筑模板木工板至关重要。其中&#xff0c;1220*2440mm规格的建筑模板木工板作为一种常见的选择&#xff0c;特别适用于混凝土工程和桥梁建设。本文将重点介绍这种规格的木工板作为覆膜板在桥梁工程中的应用。 1220*2440mm建筑模板木工板是一…

基于 golang 从零到一实现时间轮算法 (一)

前言 时间轮是用来解决海量百万级定时器&#xff08;或延时&#xff09;任务的最佳方案&#xff0c;linux 的内核定时器就是采用该数据结构实现。 应用场景 自动删除缓存中过期的 Key&#xff1a;缓存中设置了 TTL 的 kv&#xff0c;通过把该 key 对应的 TTL 以及回调方法注册…