C++ Double Dispatch,即双重调度

调度概述

对于初次接触或曾有所闻但未曾深入了解的开发人员而言,调度(Dispatching)这一概念值得我们深入探讨。调度,从字面意义上理解,即将某物发送至特定目的地。在C++编程中,调度同样指将控制权从一处转移至另一处,例如从主方法调用函数或方法时,程序会跳转至新的地址空间并执行该方法。这一过程即称为调度。

根据方法调用的性质,调度可分为静态调度(Static Dispatch)和动态调度(Dynamic Dispatch)。静态调度在编译时确定调用的函数,这种方式没有运行时开销,因为编译器在编译阶段就能确定应该调用哪个函数。相反,动态调度则在运行时确定调用的函数,这为程序提供了灵活性,允许程序根据对象的实际类型在运行时决定调用哪个函数,从而支持多态性。

在字面上,静态调度和动态调度均属于单重调度(Single Dispatch),即调度机制仅依赖于单个对象的类型来决定调用的函数。

双重调度解析

在理解了单重调度的基础上,我们进一步探讨双重调度(Double Dispatch)。双重调度意味着调度机制依赖于两个对象的类型来决定调用的函数。然而,需要注意的是,C++语言本身并不直接支持双重调度。

以下是一个尝试通过虚函数实现双重调度的示例代码:

class Bird {};
class Chicken : public Bird {};class Animal {
public:virtual void eats(Bird *b) {cout << "Animal Eats Bird" << endl;}virtual void eats(Chicken *b) {cout << "Animal Eats Chicken" << endl;}
};class Tiger : public Animal {
public:void eats(Bird *b) {cout << "Tiger Eats Bird" << endl;}void eats(Chicken *b) {cout << "Tiger Eats Chicken" << endl;}
};int main() {Animal *tiger = new Tiger();Bird *chicken = new Chicken();tiger->eats(chicken); // 输出结果并非预期的 "Tiger Eats Chicken"return 0;
}


        在上述代码中,我们期望的输出结果是“Tiger Eats Chicken”,然而实际上编译器输出的结果是“Tiger Eats Bird”。原因在于C++仅支持单重调度,即仅根据Animal指针(指向Tiger对象)来确定调用的eats方法,而未能根据Bird指针(指向Chicken对象)进行第二次调度。

        尽管C++语言本身不支持双重调度,但我们可以通过其他方式(如访问者模式)来模拟实现双重调度的效果。这些模式允许我们在运行时根据两个对象的类型来决定调用的方法,从而在一定程度上实现双重调度的功能。

使用访问者模式模拟双重调度的示例代码:

#include <iostream>
#include <memory>
#include <vector>// 基类接口,定义接受访问者的方法
class Visitable {
public:virtual ~Visitable() {}virtual void accept(std::shared_ptr<class Visitor> visitor) = 0;
};// 具体的可访问对象
class Bird : public Visitable {
public:void accept(std::shared_ptr<class Visitor> visitor) override {visitor->visit(std::static_pointer_cast<Bird>(shared_from_this()));}// 其他Bird相关的方法...
};class Chicken : public Visitable {
public:void accept(std::shared_ptr<class Visitor> visitor) override {visitor->visit(std::static_pointer_cast<Chicken>(shared_from_this()));}// 其他Chicken相关的方法...
};// 访问者接口,定义访问不同对象的方法
class Visitor {
public:virtual ~Visitor() {}virtual void visit(std::shared_ptr<Bird> bird) = 0;virtual void visit(std::shared_ptr<Chicken> chicken) = 0;
};// 具体的访问者,实现访问不同对象时的行为
class AnimalVisitor : public Visitor {
public:void visit(std::shared_ptr<Bird> bird) override {std::cout << "Animal visits Bird" << std::endl;}void visit(std::shared_ptr<Chicken> chicken) override {std::cout << "Animal visits Chicken" << std::endl;}
};class TigerVisitor : public Visitor {
public:void visit(std::shared_ptr<Bird> bird) override {std::cout << "Tiger eats Bird" << std::endl;}void visit(std::shared_ptr<Chicken> chicken) override {std::cout << "Tiger eats Chicken" << std::endl;}
};int main() {// 创建可访问对象std::shared_ptr<Bird> bird = std::make_shared<Bird>();std::shared_ptr<Chicken> chicken = std::make_shared<Chicken>();// 创建访问者std::shared_ptr<Visitor> animalVisitor = std::make_shared<AnimalVisitor>();std::shared_ptr<Visitor> tigerVisitor = std::make_shared<TigerVisitor>();// 使用访问者访问对象,模拟双重调度bird->accept(animalVisitor); // 输出: Animal visits Birdchicken->accept(animalVisitor); // 输出: Animal visits Chickenbird->accept(tigerVisitor); // 输出: Tiger eats Birdchicken->accept(tigerVisitor); // 输出: Tiger eats Chickenreturn 0;
}

        在这个例子中,Visitable是一个接口,定义了accept方法,该方法接受一个Visitor对象。BirdChicken类实现了Visitable接口,并在accept方法中调用访问者的visit方法,同时将自身(通过std::static_pointer_cast转换为正确的类型)作为参数传递。

  Visitor是一个接口,定义了访问BirdChicken对象的方法。AnimalVisitorTigerVisitor是具体的访问者类,它们实现了Visitor接口,并在visit方法中定义了访问不同对象时的行为。

        在main函数中,我们创建了BirdChicken对象,以及AnimalVisitorTigerVisitor访问者。通过调用accept方法,我们将访问者传递给可访问对象,从而模拟了双重调度的效果。根据访问者和被访问对象的类型,程序会输出相应的结果。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

103、Python并发编程:使用信号量Semaphore实现资源有限的并发场景

引言 在前面几篇文章的基础上&#xff0c;应对并发编程中现成同步的需求场景&#xff1a; 我们可以使用锁&#xff0c;作为多线程同步的几个核心基础&#xff0c;实现对临界资源的保护&#xff0c;确保满足基本的互斥访问逻辑。 使用条件变量Condition&#xff0c;实现有固定…

蛋奶烙饼:美味与温暖的邂逅

食家巷蛋奶烙饼&#xff0c;那金黄的色泽、浓郁的奶香和蛋香&#xff0c;光是看着就让人垂涎欲滴。它的制作过程并不复杂&#xff0c;却充满了生活的烟火气。将面粉、鸡蛋、牛奶等简单的食材混合在一起&#xff0c;搅拌成细腻的面糊。在平底锅中倒入少许油&#xff0c;舀一勺面…

Linux内核中IRQ Domain的结构、操作及映射机制详解

往期内容 本专栏往期内容&#xff0c;interrtupr子系统&#xff1a; 深入解析Linux内核中断管理&#xff1a;从IRQ描述符到irq domain的设计与实现 pinctrl和gpio子系统专栏&#xff1a; 专栏地址&#xff1a;pinctrl和gpio子系统 编写虚拟的GPIO控制器的驱动程序&#xff1a;…

cocos creator 3.8.3物理组件分组的坑

坑&#xff0c;坑的不行的大坑 group用的二进制的左移获取十进制的数值 目前是这样判断的&#xff0c;也不知道对不对&#xff0c;什么get、set Group没找到

如何解决“在ANACONDA prompt可以使用‘conda activate‘,但是在pycharm终端没办法使用该指令“”

一、设置好环境变量 此电脑&#xff08;右键&#xff09;-属性-高级系统设置-环境变量-在系统变量那一筐的PATH双击添加 二、完成后再测试conda activate 笔者测试完后&#xff0c;conda是可以用了&#xff0c;但是conda activate用不了。 显示该错误CommandNotFoundError:…

三菱MR-J4-B系列伺服参数一览

要点 与伺服系统控制器连接后&#xff0c;同服系统控制器的伺服参数的值即被写入各参数中。根据伺服系统控制器的机种和伺服放大器软件版本及MRConfigurator2的软件版本&#xff0c;存在无法设定的参数或范围。详细内容请参照伺服系统控制器的用户手册。请使用MR Configurator2…

Pattern program MPAT 详解

本文为VIP文章,主要介绍Pattern中元素与格式、常用指令、地址&数据产生指令等。 目录 一、pattern概述 二:Pattern构成元素 1、pattern构成元素:MPAT、END 2、pattern构成元素:pattern file name 3、pattern构成元素:SDEF 4、Pattern构成元素:REGISETR 5、Pa…

Qt编译lua库并调用

参考博客&#xff1a; 编译lua库 参考下面文章编译lua库文件 QT5.9学习笔记之QT编译lua库_qtluaintf.h-CSDN博客 https://blog.csdn.net/qq_23345187/article/details/112710677 Qt代码引用lua库文件 打开pro项目文件&#xff0c;右键空白处&#xff0c;点击添加库&#xff…

数据结构:直接插入排序

直接插入排序是数据结构中较为简单的插入排序法&#xff0c;基本思想是&#xff1a;把待排序的记录按照关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插完为止&#xff0c;得到一个新的有序序列。 实际上我们玩扑克时&#xff0c;就用了插入排…

Pr 视频过渡:溶解

效果面板/视频过渡/溶解 Video Transitions/Dissolve Adobe Premiere Pro 的视频过渡效果中&#xff0c;溶解 Dissolve效果组提供了多种平滑过渡方式&#xff0c;适用于不同的场景需求&#xff0c;从基础的淡入淡出到复杂的叠加溶解&#xff0c;帮助用户实现更具层次感的视觉过…

使用 Elasticsearch 构建食谱搜索(一)

作者&#xff1a;来自 Elastic Andre Luiz 了解如何使用 Elasticsearch 构建基于语义搜索的食谱搜索。 简介 许多电子商务网站都希望增强其食谱搜索体验。正确使用语义搜索可以让客户根据更自然的查询&#xff08;例如 “something for Valentines Day - 情人节的礼物” 或 “…

prompt资料收集

1. LANGgpt模板 # Role: 知识探索专家 ## Profile: - - 即刻App即刻App&#xff0c;享受探索、表达和创造https://m.okjike.com/originalPosts/649801f1ba47fe581a0da471?seyJ1IjoiNjQyM2IwMDE4NDg5Njk1NGJjYzhkNWU1IiwiZCI6MX0%3D2. 好的prompt的标准 主观的说&#xff1a;…

大数据学习10之Hive高级

1.Hive高级 将大的文件按照某一列属性进行GROUP BY 就是分区&#xff0c;只是默认开窗存储&#xff1b; 分区是按行&#xff0c;如一百行数据&#xff0c;按十位上的数字分区&#xff0c;则有十个分区&#xff0c;每个分区里有十行&#xff1b; 分桶是根据某个字段哈希对桶数取…

前端Nginx的安装与应用

目录 一、前端跨域方式 1.1、CORS(跨域资源共享) 1.2、JSONP(已过时) 1.3、WebSocket 1.4、PostMessage 1.5、Nginx 二、安装 三、应用 四、命令 4.1、基本操作命令 4.2、nginx.conf介绍 4.2.1、location模块 4.2.2、反向代理配置 4.2.3、负载均衡模块 4.2.4、通…

Mit6.S081-实验环境搭建

Mit6.S081-实验环境搭建 注&#xff1a;大家每次做一些操作的时候觉得不太保险就先把虚拟机克隆一份 前言 qemu&#xff08;quick emulator&#xff09;&#xff1a;这是一个模拟硬件环境的软件&#xff0c;利用它可以运行我们编译好的操作系统。 准备一个Linux系统&#xf…

AWS账号安全:如何防范与应对账号被盗风险

在云计算时代&#xff0c;Amazon Web Services&#xff08;AWS&#xff09;作为全球领先的云服务提供商&#xff0c;为企业和个人提供了强大的计算资源和灵活的服务。然而&#xff0c;随着云计算的普及&#xff0c;AWS账号被盗的风险也随之增加。我们九河云有多年用云经验&…

IPTABLE:Linux下的网络防火墙

IPTABLE&#xff1a;Linux下的网络防火墙 引言 在Linux系统中&#xff0c;IPtable是一种强大的网络防火墙工具&#xff0c;广泛应用于各种网络环境中。它不仅可以实现基本的包过滤功能&#xff0c;还能进行网络地址转换&#xff08;NAT&#xff09;、数据包记录、流量统计等高…

Java版工程行业管理系统源码-专业的工程管理软件- 工程项目各模块及其功能点清单

工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&am…

友思特应用 | 动态捕捉:高光谱相机用于移动产线上的食品检测

导读 高光谱成像技术能够为食品安全助力。以友思特BlackIndustry SWIR 1.7 Max 为代表的高光谱相机&#xff0c;完美解决了移动产线检测的应用难点。 高光谱技术&#xff1a;为食品安全保驾护航 食品安全一直是大众关心的热点话题&#xff0c;提供安全、高质量的食品需要对食…

Java——》try-with-resource

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…