当前位置: 首页 > news >正文

【模板的特殊继承关系】 奇异的递归模板模式

一、奇异的递归模板模式范例

奇异的递归模板模式 ( C u r i o u s l y R e c u r r i n g T e m p l a t e P a t t e r n ) (Curiously \ Recurring \ Template \ Pattern) (Curiously Recurring Template Pattern)不是一种新技术,而是一种模板编程中使用的编程手法:把派生类作为基类的模板参数。

下面是一个简单范例:

//基类
template<typename T>
class Base {//...
};//派生类1
class Derived1 : public Base<Derived1> { //派生类继承基类的派生类参数的泛型
public:void myfunc() {std::cout << "Derived1::myfunc()执行了!\n";}};//派生类2(模板)
template<typename T>
class Derived2 : public Base<Derived2<T>> {};

这样的继承方式在模板与泛型编程中很常见。
以下介绍一下基本的几个用法。

二、在基类中使用派生类对象

一般情况下,我们可以通过 s t d : : s t a t i c _ c a s t < T > std::static\_cast<T> std::static_cast<T>静态类型转换将基类对象转换为派生类对象,从而在基类对象内使用派生类对象的接口。
如下代码所示:

template<typename T>
class Base {private:Base() {}friend T; //只有在类内实例化Base<T>的T是友元public:void asDerived() {T& derived = static_cast<T&>(*this); //将基类转为派生类derived.myfunc();}
};//派生类1
class Derived1 : public Base<Derived1> { //派生类继承基类的派生类参数的泛型
public:void myfunc() {std::cout << "Derived1::myfunc()执行了!\n";}};

为了防止继承时出错,我们在基类内增加一个友元类 T T T,只对相同派生类的类模板开放构造函数。

使用友元可以避免下面的错误:
在这里插入图片描述

相应的友元模板知识点可以参考:友元类模板,这里就不赘述了。
注意这里的写法:
在这里插入图片描述
当然,这个派生类也可以是一个模板:

//派生类2(模板)
template<typename T>
class Derived2 : public Base<Derived2<T>> {
public:void myfunc() {std::cout << "Derived2<T>::myfunc()执行了!\n";}
};

同时,我们在基类增加友元声明:

template<typename U>
friend class Derived2; // 允许所有 Derived2 的实例成为友元

这样我们实例化出的派生类都支持基类的私有访问了,调用下方测试代码:

void Test1() {Derived1 myd1;myd1.asDerived();Derived2<int> myd2;myd2.asDerived();
}

这样的运行结果是,我们在基类调用了派生类的方法,也就是可以看作是派生类为基类提供了接口,这与一般的继承调用不同(基类给派生类提供接口)
在这里插入图片描述运行结果如下:
在这里插入图片描述

三、基于减少派生类代码量的考虑

通常,如果我们需要在派生类内增加功能,我们需要不断的补充代码,而这里的做法是,我们把一部分代码放入基类实现,让派生类内的代码相对简洁一些。

下面是一个例子:

//基于减少派生类中代码量的考虑template<typename T>
struct shape {//友元实现运算符重载friend bool operator==(const shape<T>& obj1, const shape<T>& obj2) {const T& objtmp1 = static_cast<const T&>(obj1);const T& objtmp2 = static_cast<const T&>(obj2);if (!(objtmp1 < objtmp2) && !(objtmp2 < objtmp1)) return true;return false;}
};struct square :public shape<square> {int sidelength;square(int length) :sidelength(length) {}};//全局实现运算符重载
bool operator<(square const& obj1, square const& obj2) {return obj1.sidelength < obj2.sidelength;
}

我们需要在 s q u a r e square square派生类里面增加一个运算符重载,我们可以把这一个操作放入基类中来实现,或者放入全局来实现。
这里详细说一下在基类实现:

1.我们需要将运算符设置为友元的,这样我们就能在派生类内访问到基类的运算符了。这样设置的友元是全局的,这在之前的友元章节有详细说明。

2.然后我们需要使用 s t d : : s t a t i c _ c a s t < T > std::static\_cast<T> std::static_cast<T>对传入的基类对象转为相应的派生类对象,最后进行比较即可。

调用测试代码:

void Test2() {square obj1(15), obj2(20), obj3(20);if (obj1 == obj2) {std::cout << "obj1 和obj2相等\n";}else {std::cout << "obj1 和obj2不相等\n";}if (obj2 == obj3) {std::cout << "obj2 和obj3相等\n";}else {std::cout << "obj2 和obj3不相等\n";}

这样,我们在派生类中无需添加任何代码,而将代码加入基类或全局。

在这里插入图片描述

三、基类调用派生的接口与多态的体现

就像上面说的,这样的奇异递归模板模式可以让基类调用派生类的接口。
下面我们看看具体的实现:

//基类
template<typename T>
class Human {
private:Human() {}friend T;public:T& toChild() {return static_cast<T&>(*this); //基类转派生类}void parenteat() {toChild().eat();}};//派生类1
class Men :public Human<Men> {
public:void eat() {std::cout << "男人喜欢吃面食!\n";}
};//派生类2
class Women :public Human<Women> {
public:void eat() {std::cout << "女人喜欢吃米饭!\n";}
};

在基类中,核心代码如下:
在这里插入图片描述
首先是转为派生类,然后调用派生类接口

Men mymen;
Women mywomen;//派生类给基类提供接口
mymen.parenteat();
mywomen.parenteat();

运行后我们得到:

在这里插入图片描述
这里实现了让基类调用我们派生类的接口,或者说是让派生类给基类提供接口。

我们也能在此基础上,添加一个函数模板:

template<typename T>
void myHumanFuncTest(Human<T>& tmpobj) {tmpobj.toChild().eat();
}

调用:

Men mymen;
Women mywomen;
myHumanFuncTest(mymen);
myHumanFuncTest(mywomen);

得到相同的结果:
在这里插入图片描述
实际上,可以发现,这是多态的体现,也是前面我们说过的静态多态

这样的编程方法可以让我们更灵活:比如当 e a t ( ) eat() eat()函数是一个静态成员函数或者函数模板的时候,我们就无法使用虚函数进行动态多态,因此就必须使用奇异的递归模板模式了。

相关文章:

【模板的特殊继承关系】 奇异的递归模板模式

一、奇异的递归模板模式范例 奇异的递归模板模式 ( C u r i o u s l y R e c u r r i n g T e m p l a t e P a t t e r n ) (Curiously \ Recurring \ Template \ Pattern) (Curiously Recurring Template Pattern)不是一种新技术&#xff0c;而是一种模板编程中使用的编程手…...

SAP B1 单据页面自定义 - 用户界面编辑字段

背景 接《SAP B1 基础实操 - 用户定义字段 (UDF)》&#xff0c;在设置完自定义字段后&#xff0c;如下图&#xff0c;通过打开【用户定义字段】可打开表单右侧的自定义字段页。然而再开打一页附加页面操作繁复&#xff0c;若是客户常用的定义字段&#xff0c;也可以把这些用户…...

MinIO【部署 02】Linux集群版本及Windows单机版、单机多目录版、分布式版(cmd启动脚本及winsw脚本分享)

Linux集群版及Windows单机版分布式版 1.Linux集群版1.1 安装启动停止1.2 将MinIO添加到服务 2.Windows2.1 官网安装2.2 本地测试2.2.1 cmd启动脚本2.2.2 winsw脚本 3.总结 1.Linux集群版 官网下载地址 https://min.io/download#/linux&#xff1b; 官网安装文档 https://min.i…...

手握18个大厂offer,我在大模型风口起飞

前言 在“金三银四”这一招聘旺季中&#xff0c;社交媒体上满是分享 offer 信息的“求助帖”。这些帖子通常只公布公司名称与薪资区间&#xff0c;而将具体岗位模糊化&#xff0c;以此作为判断岗位是否值得入职的衡量标准。 2024 年毕业的 985 硕士白丁&#xff08;化名&…...

邦芒忠告:办公室聊天应避开的四个话题

职场人生风云变幻&#xff0c;害人之心不可有&#xff0c;防人之心不可无。千万别把同事当知己&#xff0c;无话不谈&#xff0c;把自己的私域圈起来当成办公室话题的禁区&#xff0c;轻易不让人涉足&#xff0c;其实是非常明智的一招&#xff0c;是竞争压力下的自我保护。 话题…...

交易型开放式指数基金(ETF)

交易型开放式指数基金&#xff08;Exchange Traded Fund&#xff0c;简称 ETF&#xff09;是一种投资工具&#xff0c;以下是用通俗易懂的语言对其进行的讲解&#xff1a; 一、基本概念 想象 ETF 是一个大篮子&#xff0c;里面装着很多不同的东西。在金融市场里&#xff0c;这…...

opencv将灰度图转为彩色图片

文章目录 背景灰度图优势opencv读取灰度图彩色转灰度算法需求 方法测试代码 背景 在图像处理中通常需要将图片转为灰度图 灰度图&#xff0c;也称为灰度图像或黑白图像&#xff0c;是一种只包含亮度信息而不包含颜色信息的图像。在灰度图中&#xff0c;每个像素的亮度级别通常…...

判断PDF与图片是否可以预览

一、判断图片是否可以预览 在JavaScript中&#xff0c;可以使用Image对象来判断一个图片URL是否可以访问。如果图片可以被加载&#xff0c;那么load事件会被触发&#xff1b;如果图片无法访问&#xff0c;error事件会被触发。 function checkImageAccessibility(url, callbac…...

多线程与并发区别

在Java中&#xff0c;多线程与并发是两个既相关又有所区别的概念。我们可以这样来理解它们&#xff1a; 多线程&#xff08;Multi-threading&#xff09;&#xff1a; 多线程是指程序能够同时执行多个线程。每个线程都是一个独立的执行流&#xff0c;它们共享程序的内存空间&a…...

这个桌面日历真不错 笔记 提醒 生日记录 打卡 翻译都有 真的太方便了!

这个桌面日历真不错 笔记 提醒 生日记录 打卡 翻译都有 真的太方便了&#xff01;日历产品非常的多&#xff0c;如何选择一个合适自己的桌面日历&#xff0c;这个很重要&#xff0c;今天小编给大家介绍这个芝麻日历&#xff0c;一起看下它有些什么功能&#xff0c;是不是你需要…...

多模态大语言模型综述(中)-算法实用指南

本文是Multimodal Large Language Models: A Survey的译文之算法实用指南部分。 上&#xff1a;摘要、概念与技术要点实用指南中&#xff1a;算法实用指南(本文)下: 任务的实用指南(应用)、挑战等 原始信息 标题: Multimodal Large Language Models: A Survey译文: 多模态大…...

Qt | ubuntu20.04安装Qt6.5.3并创建一个example完整教程(涉及诸多开发细节,商用慎重)

点击上方"蓝字"关注我们 01、下载 >>> 下载Qt在线安装包 这里采用镜像地址进行下载,避免网络过慢。 镜像地址:http://mirrors.ustc.edu.cn/qtproject/archive/online_installers/4.5/ 选择最新版本下载,如截至目前最新版本为qt-unified-linux-x64-4.5.2…...

苏州科技大学、和数联合获得国家知识产权局颁发的3项发明专利证书

近日&#xff0c;基于“苏州科技大学-和数智能软件区块链技术工程实验室”的研究成果&#xff0c;国家知识产权局正式授权了苏州科技大学、苏州和数区块链应用研究院联合申报的3项发明专利证书。 分别为&#xff1a; 一种基于双账本的物联网数据存储与共享方法 一种面向物联网…...

CleanMyMac X2024破解版mac电脑清理工具

今天&#xff0c;我要跟大家分享一个让我彻底告别电脑卡顿的秘密武器——CleanMyMac X。这不仅仅是一款普通的清理工具&#xff0c;它是你电脑的私人健身教练&#xff0c;让电脑焕发青春活力&#xff01; CleanMyMac直装官方版下载地址&#xff1a; http://wm.makeding.com/i…...

微软数据库的SQL注入漏洞解析——Microsoft Access、SQLServer与SQL注入防御

说明:本文仅是用于学习分析自己搭建的SQL漏洞内容和原理,请勿用在非法途径上,违者后果自负,与笔者无关;本文开始前请认真详细学习《‌中华人民共和国网络安全法》‌及其相关法规内容【学法时习之丨网络安全在身边一图了解网络安全法_中央网络安全和信息化委员会办公室】 。…...

无人机之处理器篇

无人机的处理器是无人机系统的核心部件之一&#xff0c;它负责控制无人机的飞行、数据处理、任务执行等多个关键功能。以下是对无人机处理器的详细解析&#xff1a; 一、处理器类型 无人机中使用的处理器主要包括以下几种类型&#xff1a; CPU处理器&#xff1a;CPU是无人机的…...

828华为云征文 | 华为云Flexus X实例上实现Docker容器的实时监控与可视化分析

前言 华为云Flexus X&#xff0c;以顶尖算力与智能调度&#xff0c;引领Docker容器管理新风尚。828企业上云节之际&#xff0c;Flexus X携手前沿技术&#xff0c;实现容器运行的实时监控与数据可视化&#xff0c;让管理变得直观高效。无论是性能瓶颈的精准定位&#xff0c;还是…...

缓存预热/雪崩/穿透/击穿

1. 缓存预热 预先将MySQL中的数据同步至Redis的过程 2. 缓存雪崩 Redis主机出现故障&#xff0c;或有大量的key同时过期大面积失效导致Redis不可用 Redis中key设置为永不过期&#xff0c;或者过期时间错开Redis缓存集群实现高可用多缓存结合预防雪崩服务降级 3. 缓存穿透 …...

C/C++:优选算法

一、双指针 1.1移动零 链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操…...

用于大数据分析的数据存储格式:Parquet、Avro 和 ORC 的性能和成本影响

高效的数据处理对于依赖大数据分析做出明智决策的企业和组织至关重要。显著影响数据处理性能的一个关键因素是数据的存储格式。本文探讨了不同存储格式&#xff08;特别是 Parquet、Avro 和 ORC&#xff09;对 Google Cloud Platform &#xff08;GCP&#xff09; 上大数据环境…...

【Jupyter Notebook】安装与使用

打开Anaconda Navigator点击"Install"&#xff08;Launch安装前是Install&#xff09;点击"Launch" 点击"File"-"New"-"Notebook" ​ 5.点击"Select"选择Python版本 6.输入测试代码并按"EnterShift"运…...

默认端口被占用后,如何修改Apache2 端口

你可以通过以下步骤修改 Apache2 的默认端口&#xff08;80 端口&#xff09;&#xff1a; 1. 修改 Apache2 配置文件 首先&#xff0c;你需要编辑 Apache2 的端口配置文件&#xff1a; sudo nano /etc/apache2/ports.conf在文件中&#xff0c;你会看到类似以下的内容&#…...

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(2) (*****生成数据结构类的方式特别有趣****)

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(1)-CSDN博客 本节内容 实现目标 通过已经得到的Excel表格…...

Idea 中的一些配置

配置 javap jdk 自带的 javap 可以用来查看字节码信息。 配置过程&#xff1a; 打开设置&#xff0c;定位到 Tools&#xff0c;External Tools新建项&#xff0c;Program 中填 javap 的路径Argument 中填 -c $FileClass$Working directory 中填 $OutputPath$ Argument 中也…...

VulnHub DC-1-DC-7靶机WP

VulnHub DC系列靶机&#xff1a;https://vulnhub.com/series/dc,199/ # VulnHub DC-1 nmap开路获取信息 Nmap scan report for 192.168.106.133 Host is up (0.00017s latency). Not shown: 997 closed ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 1…...

基于DPU的容器冷启动加速解决方案

1. 方案背景 1.1. 业务背景 随着容器技术的迅猛发展与广泛应用&#xff0c;一种新的云计算服务模式应运而生-函数即服务&#xff08;FaaS, Function as a Service&#xff09;。FaaS作为一种无服务器&#xff08;Serverless&#xff09;计算方式&#xff0c;极大地简化了开发…...

SOME/IP 通信协议详细介绍

标签&#xff1a; SOME/IP 通信协议详细介绍&#xff1b; SOME/IP&#xff1b; SOME/IP 通信协议详细介绍 SOME/IP 通信协议详细介绍 官网&#xff1a; https://some-ip.com/ 1. 什么是SOME/IP&#xff1f; SOME/IP&#xff08;Scalable service-Oriented MiddlewarE over IP…...

基于Boost库的搜索引擎开发实践

目录 1.项目相关背景2.宏观原理3.相关技术栈和环境4.正排、倒排索引原理5.去标签和数据清洗模块parser5.1.认识标签5.2.准备数据源5.3.编写数据清洗代码parser5.3.1.编写读取文件Readfile5.3.2.编写分析文件Anafile5.3.2.编写保存清洗后数据SaveHtml5.3.2.测试parser 6.编写索引…...

【2023年】云计算金砖牛刀小试3

A场次题目:OpenStack平台部署与运维 业务场景: 某企业拟使用OpenStack搭建一个企业云平台,用于部署各类企业应用对外对内服务。云平台可实现IT资源池化,弹性分配,集中管理,性能优化以及统一安全认证等。系统结构如下图: 企业云平台的搭建使用竞赛平台提供的两台云服务…...

在以太坊中不同合约之间相互调用的场景有哪些?

在以太坊中&#xff0c;合约调用合约的场景有很多&#xff0c;以下是一些常见的情况&#xff1a; 一、复杂业务逻辑的拆分 模块化设计&#xff1a; 当一个智能合约的业务逻辑变得复杂时&#xff0c;可以将其拆分为多个较小的合约&#xff0c;每个合约负责特定的功能。例如&…...