当前位置: 首页 > 编程日记 > 正文

C++ 通过模版工厂实现 简单反射机制

前言

我们知道Java/Python这种语言能够很好得 支持反射。反射机制 就是一种用户输入的字符串到对应实现方法的映射,比如http接口中 用户传入了url,我们需要调用该url对应的方法/函数对象 从而做出对应的操作。

而C++ 并没有友好得支持这样的操作,而最近工作中需要通过C++实现http接口,这个过程想要代码实现得优雅一些,就需要实现这样的功能。

最终的实现结果就是:

 int main() {Initial();JudgeName jm;auto res = jm.Judge("Zhang");res->ShowName();return 0;
}

我们只需要通过输入一个字符串,就能得到一个字符串对应的类的对象,从而调用该对象对应的方法。

实现过程

  1. 实现模版工厂
    反射机制本身是依赖工厂模式实现的,也就是提供一个创建函数对象的工厂,能够创建各种类型的函数对象。
    这里面的各种类型 – 其实就需要实现一个通用的模版工厂来达到创建各种类型的目的。

    // 工厂类模版,创建各种类型的类对象
    template <class OperationType_t>
    class OperationFactory {
    public:// 创建一个工厂单例,只会有一个对象工厂static OperationFactory<OperationType_t>& Instance() {static OperationFactory<OperationType_t> instance;return instance;}private:// We don't allow to constructor, copy constructor and align constructorOperationFactory() = default;~OperationFactory() = default;OperationFactory(const OperationFactory&) = delete;const OperationFactory& operator= (const OperationFactory&) = delete;};
    
  2. 实现类的对象到类名的映射。
    我们实现了能够创建所有类对象的函数工厂,但是还需要将创建好的对象和他们的类名字对应起来,做一个映射。
    在模版工厂类中补充映射逻辑:

    // Factory class template
    template <class OperationType_t>
    class OperationFactory {
    public:// Single pattern of the factorystatic OperationFactory<OperationType_t>& Instance() {static OperationFactory<OperationType_t> instance;return instance;}// 类名 和 类对象的映射void RegisterOperation(const std::string& op_name,OperationRegister<OperationType_t>* reg) {operationRegister[op_name] = reg;}// 获取指定类名字 对应的 对象// 也就是从映射map中查找OperationType_t* GetOperation(const std::string& op_name) {if (operationRegister.find(op_name) != operationRegister.end()) {return operationRegister[op_name]->CreateOperation(op_name);}return nullptr;}private:// We don't allow to constructor, copy constructor and align constructorOperationFactory() = default;~OperationFactory() = default;OperationFactory(const OperationFactory&) = delete;const OperationFactory& operator= (const OperationFactory&) = delete;// 类名字 和 类对象之间的映射std::map<std::string, OperationRegister<OperationType_t>* > operationRegister;
    };
    
  3. 实现一个注册类
    此时 我们通过这个注册类的入口 将类名 和类的对象完整映射起来。

    template <class OperationType_t>
    class OperationRegister {
    public:virtual OperationType_t* CreateOperation(const std::string& op_name) = 0;protected:OperationRegister() {}virtual ~OperationRegister() {}
    };// 注册一个以基类的子类对象。
    template <class OperationType_t, class OperationImpl_t>
    class OperationImplRegister : public OperationRegister<OperationType_t> {
    public:// 获取一个基类的工厂explicit OperationImplRegister(const std::string& op_name) {OperationFactory<OperationType_t>::Instance().RegisterOperation(op_name, this);}// 获取这个基类对应的子类对象OperationType_t* CreateOperation(const std::string& op_name) {return new OperationImpl_t(op_name);}
    };
    

应用

有了上面的模版工厂 以及 注册类名字 和 类的映射模版,接下来我们看看怎么使用:

我们实现了如下一个父类 以及 几个父类对应的子类实现

class Father {
public:virtual std::string Name() = 0;virtual void ShowName() = 0;
};class Wang : public Father{
public:Wang(const std::string& name) : name_(name){}std::string Name() override { return name_; }void ShowName() override { cout << "Name: " << name_ << endl; }private:std::string name_;
};class Zhang : public Father {
public:Zhang(const std::string& name) : name_(name) {}std::string Name() override { return name_; }void ShowName() override { cout << "Name: " << name_ << endl; }private:std::string name_;
};class Li : public Father {
public:Li(const std::string& name) : name_(name) {}std::string Name() override { return name_; }void ShowName() override { cout << "Name: " << name_ << endl; }private:std::string name_;
};

接下来需要统一注册一下这几个子类 的 名字 以及他们的对象:

// Construct the string name with there object's map
void Initial() {static bool init = false;if (init == false) {static OperationImplRegister<Father, Wang> wang("Wang");static OperationImplRegister<Father, Zhang> zhang("Zhang");static OperationImplRegister<Father, Li> li("Li");init = true;}
}

最后就是一个通过字符串 获取对象

class JudgeName {
public:JudgeName(){}Father* Judge(const std::string& name) {OperationFactory<Father>& fac = OperationFactory<Father>::Instance();return fac.GetOperation(name);}
};

这个类的函数中,我们用户只需要传入一个字符串,就能够得到这个字符串的类的对象,从而进行后续的对象方法相关的操作。

能够极大得简化我们的if-else if 这样的分支代码,同时提升了代码的可扩展性。用户只需要继续补充想要使用的类,并将这个类的名字和对象注册到映射map中就好了,后续只需要使用类名就能够创建出这个类的对象。

完整代码实现

#include <iostream>
#include <map>using namespace std;// Register the operation
// The 'OperationTyple_t' is the abstract class
template <class OperationType_t>
class OperationRegister {
public:virtual OperationType_t* CreateOperation(const std::string& op_name) = 0;protected:OperationRegister() {}virtual ~OperationRegister() {}
};// Factory class template
template <class OperationType_t>
class OperationFactory {
public:// Single pattern of the factorystatic OperationFactory<OperationType_t>& Instance() {static OperationFactory<OperationType_t> instance;return instance;}void RegisterOperation(const std::string& op_name,OperationRegister<OperationType_t>* reg) {operationRegister[op_name] = reg;}OperationType_t* GetOperation(const std::string& op_name) {if (operationRegister.find(op_name) != operationRegister.end()) {return operationRegister[op_name]->CreateOperation(op_name);}return nullptr;}private:// We don't allow to constructor, copy constructor and align constructorOperationFactory() = default;~OperationFactory() = default;OperationFactory(const OperationFactory&) = delete;const OperationFactory& operator= (const OperationFactory&) = delete;std::map<std::string, OperationRegister<OperationType_t>* > operationRegister;
};// An template class to create the detail Operation
template <class OperationType_t, class OperationImpl_t>
class OperationImplRegister : public OperationRegister<OperationType_t> {
public:explicit OperationImplRegister(const std::string& op_name) {OperationFactory<OperationType_t>::Instance().RegisterOperation(op_name, this);}OperationType_t* CreateOperation(const std::string& op_name) {return new OperationImpl_t(op_name);}
};class Father {
public:virtual std::string Name() = 0;virtual void ShowName() = 0;
};class Wang : public Father{
public:Wang(const std::string& name) : name_(name){}std::string Name() override { return name_; }void ShowName() override { cout << "Name: " << name_ << endl; }private:std::string name_;
};class Zhang : public Father {
public:Zhang(const std::string& name) : name_(name) {}std::string Name() override { return name_; }void ShowName() override { cout << "Name: " << name_ << endl; }private:std::string name_;
};class Li : public Father {
public:Li(const std::string& name) : name_(name) {}std::string Name() override { return name_; }void ShowName() override { cout << "Name: " << name_ << endl; }private:std::string name_;
};// Construct the string name with there object's map
void Initial() {static bool init = false;if (init == false) {static OperationImplRegister<Father, Wang> wang("Wang");static OperationImplRegister<Father, Zhang> zhang("Zhang");static OperationImplRegister<Father, Li> li("Li");init = true;}
}class JudgeName {
public:JudgeName(){}Father* Judge(const std::string& name) {OperationFactory<Father>& fac = OperationFactory<Father>::Instance();return fac.GetOperation(name);}
};int main() {Initial();JudgeName jm;auto res = jm.Judge("Zhang");res->ShowName();return 0;
}

相关文章:

计算机世界的“十六进制”为什么如此重要

在计算机世界中,十六进制扮演着不可或缺的角色。它以其紧凑的表示形式、与二进制的天然对应关系以及在各个领域的广泛应用,成为了计算机科学中的一把重要工具。总体而言,计算机需要十六进制并非偶然,它是一种为了更好地满足人类理解和处理数据的需求而产生的工具,为计算机科学的发展和应用提供了便利和支持。

面试官:如何实现10亿数据判重?

以 Java 中的 int 为例,来对比观察 BitMap 的优势,在 Java 中,int 类型通常需要 32 位(4 字节*8),而 BitMap 使用 1 位就可以来标识此元素是否存在,所以可以认为 BitMap 占用的空间大小,只有 int 类型的 1/32,所以有大数据量判重时,使用 BitMap 也可以实现。所以数据库去重显然是不行的。而使用集合也是不合适的,因为数据量太大,使用集合会导致内存不够用或内存溢出和 Full GC 频繁等问题,所以此时我们的解决方案通常是采用布隆过滤器来实现判重。

Java项目:校园二手市场系统(java+SSM+mysql+maven+tomcat)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目简述&#xff08; IW文档&#xff09; 功能&#xff1a;本系统分用户前台和管理员后台。 本系统用例模型有三种&#xff0c;分别是游客、注册用户和系统管 理员。下面分别对这三个角色的功能进行描…

php中$_REQUEST、$_POST、$_GET的区别和联系小结

php中$_REQUEST、$_POST、$_GET的区别和联系小结 作者&#xff1a; 字体&#xff1a;[增加 减小] 类型&#xff1a;转载php中有$_request与$_post、$_get用于接受表单数据&#xff0c;当时他们有何种区别&#xff0c;什么时候用那种最好。1. $_REQUEST php中$_REQUEST可以获取以…

uva 315 (poj 1144 求割点)

题意&#xff1a;给你一张无向图&#xff0c;求割点的个数。 思路&#xff1a;输入稍微处理一下接着直接套模版。 1 #include <iostream>2 #include <cstdio>3 #include <cstring>4 #include <cstdlib>5 #include <cmath>6 #include <algorit…

SQL学习之计算字段的用法与解析

一、计算字段 1、存储在数据库表中的数据一般不是应用程序所需要的格式。大多数情况下,数据表中的数据都需要进行二次处理。下面举几个例子。 (1)、我们需要一个字段同时显示公司名和公司地址&#xff0c;但这两个信息存储在不同表的列中。 (2)、省份、城市、邮政编码存储在不同…

手把手教你 用C++实现一个 可持久化 的http_server

前言 本文介绍一个有趣的 通过C实现的 持久化的http_server demo&#xff0c;这样我们通过http通信之后的数据可以持久化存储&#xff0c;即使server挂了&#xff0c;数据也不会丢失。我们的http_sever 也就能够真正得作为一个后端server了。 本身持久化这个能力是数据库提供…

【SVN多用户开发】代码冲突解决办法

SVN是一款集中式的代码存储工具&#xff0c;可以帮助多个用户协同开发同一应用程序。 但是SVN不能完全代替人工操作&#xff0c;有时也需要程序员自己进行沟通确认有效的代码。 下面就简单的看一下&#xff0c;常见的代码冲突以及解决方法。 总结起来&#xff0c;无非是&#x…

Java项目:在线宠物商店系统(java+SSM+mysql+maven+tomcat)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目简述 功能&#xff1a;本系统分用户前台和管理员后台。 系统包括用户的注册登录&#xff0c;狗狗的展示购物车添加以及下 单支付购买&#xff0c;后台有管理员用户&#xff0c;可以操作狗狗的品种&…

字符串中的数字排序

2019独角兽企业重金招聘Python工程师标准>>> public static String getBusiScope(String busiScope){ String regex "\\d{1,2}"; String busiStr""; Pattern pattern Pattern.compile(regex); Matcher matcher pattern.matcher(busiScope…

oo第二单元总结

第二单元总结 第一次作业 一、设计策略 本次作业采用FAFS算法&#xff0c;可直接用输入线程与电梯线程交互&#xff0c;调度器暂时不需要参与&#xff0c;故一共设计三个类三线程&#xff1a;Main类、elevator类及input类&#xff0c;main线程、elevator线程及input线程。main线…

Rocksdb iterator 的 Forward-scan 和 Reverse-scan 的性能差异

前言 最近在读 MyRocks 存储引擎2020年的论文&#xff0c;因为这个存储引擎是在Rocksdb之上进行封装的&#xff0c;并且作为Facebook 内部MySQL的底层引擎&#xff0c;用来解决Innodb的空间利用率低下 和 压缩效率低下的问题。而且MyRocks 在接入他们UDB 之后成功达成了他们的…

Java知多少(29)覆盖和重载

在类继承中&#xff0c;子类可以修改从父类继承来的方法&#xff0c;也就是说子类能创建一个与父类方法有不同功能的方法&#xff0c;但具有相同的名称、返回值类型、参数列表。如果在新类中定义一个方法&#xff0c;其名称、返回值类型和参数列表正好与父类中的相同&#xff0…

Java项目:清新论坛系统(java+SSM+mysql+maven+tomcat)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目简述 功能&#xff1a;本系统分用户前台和管理员后台。 用户前台主要功能有&#xff1a; 用户注册 用户登录 浏览帖子 回复帖子 修改个人资料 管理员后台的功能有&#xff1a; 管理论坛版块 用户管…

JUnit4.11 理论机制 @Theory 完整解读

最近在研究JUnit4&#xff0c;大部分基础技术都是通过百度和JUnit的官方wiki学习的&#xff0c;目前最新的发布版本是4.11&#xff0c;结合代码实践&#xff0c;发现官方wiki的内容或多或少没有更新&#xff0c;Theory理论机制章节情况尤为严重&#xff0c;不知道这章wiki对应的…

树链剖分——线段树区间合并bzoj染色

线段树区间合并就挺麻烦了&#xff0c;再套个树链就更加鬼畜&#xff0c;不过除了代码量大就没什么其他的了。。 一些细节&#xff1a;线段树每个结点用结构体保存&#xff0c;pushup等合并函数改成返回一个结构体&#xff0c;这样好写一些 struct Seg{int lc,rc,tot;Seg(){lcr…

MyRocks: 为facebool 的社交图谱服务的LSM-tree存储引擎

文章目录概览1. UDB 架构2. UDB 表格式3. Rocksdb&#xff1a;针对flash存储优化过的第三方库3.1 Rocksdb架构3.2 为什么选择Rocksdb4. MyRocks / Rocksdb 开发历程4.1 设计目标4.2 性能挑战4.2.1 降低CPU的消耗4.2.2 降低range-scan 的延时消耗4.2.3 磁盘空间和Compaction 的一…

Java项目:精品酒店管理系统(java+SSM+mysql+maven+tomcat)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目简述 功能&#xff1a;主要功能主要功能会员管理&#xff0c;住客管理&#xff0c;房间管 理&#xff0c;系统管理&#xff0c;以及一些重要数据的展示导出维护等等; 二、项目运行 环境配置&…

iOS自动布局一

Align: Pin&#xff1a; 转载于:https://www.cnblogs.com/123qw/p/4404167.html

C#实现路由器断开连接,更改公网ip

publicstaticvoidDisconnect(){stringurl "断 线"; stringuri "http://192.168.1.1/userRpm/StatusRpm.htm?Disconnect"System.Web.HttpUtility.UrlEncode(url, System.Text.Encoding.GetEncoding("gb2312")) "&wan1"; str…

Go中的iota

当时在学习Iota这个知识点的时候仅仅是一笔掠过&#xff0c;比如这种 const(aiotab c) 一眼看出他怎么使用的时候就觉得自己已经懂得了 再到后来看到这样的例子 const&#xff08;a 5*iotab c &#xff09;以及 const&#xff08;a 1<<(10*iota)bc &#xff09; 第一…

从 SSLTLS 的底层实现来看 网络安全的庞大复杂体系

文章目录前言1. HTTP协议通信的问题1.1 tcpdump 抓取http 请求包1.2 报文分析1.3 HTTP 协议问题2. SSL & TLS 协议的基本介绍和历史演进3. TLS 1.2 实现加密传输的过程3.1 TLS HandShake 协议概览3.2 第一次握手&#xff1a;ClientHello3.3 第二次握手&#xff1a;从Server…

UICollectionView

UICollectionView 多列的UITableView,最简单的形式&#xff0c;类似于iBooks中书架的布局&#xff0c;书架中放着你下载的和购买的电子书。 最简单的UICollectionView是一个GridView&#xff0c;可以多列的方式进行展示。 包含三部分&#xff0c;都是UIView的子类&#xff1a; …

Java项目:课程资源管理+在线考试平台(java+SSH+mysql+maven+tomcat)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目简述 功能包括&#xff1a; 管理员可以增删改查教材、教材商、入库教材、用户(用 户包括学生和教师)可以对教材商、教材进行。xcel的导入 导出操作。教师可以领取入库的教材&#xff0c;可以退还教…

python twisted 笔记

2019独角兽企业重金招聘Python工程师标准>>> 1.Twisted框架构建简单的C/S 要写一个基于twisted框架的服务器&#xff0c;你要实现事件处理器&#xff0c;它处理诸如一个新的客户端连接、新的数据到达和客户端连接中断等情况。 在Twisted中,你的事件处理器定义在一个…

决策树J48算法

1. J48原理 2. 举例 3. 总结 1. J48原理 基于从上到下的策略&#xff0c;递归的分治策略&#xff0c;选择某个属性放置在根节点&#xff0c;为每个可能的属性值产生一个分支&#xff0c;将实例分成多个子集&#xff0c;每个子集对应一个根节点的分支&#xff0c;然后在每个分支…

分布式系统 一致性模型的介绍 以及 zookeeper的 “线性一致性“ 讨论

文章目录1. 一致性 概览1.1 分布式系统的 “正确性”1.2 线性一致性(Linearizability)1.3 顺序一致性(Sequential consistency)1.4 因果一致性(Casual consistency)1.5 最终一致性(Eventual consistency)2. Zookeeper 的 “线性一致性” 问题3. 参考一致性算是分布式系统的定位…

Java项目:(小程序)全套商城系统(spring+spring mvc+mybatis+layui+微信小程)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 一、项目简述 本系统功能包括: 商品模块: 商品添加、规格设置&#xff0c;商品上下架等 订单模块: 下单、购物车、支付&#xff0c;发货、收货、评 退款等 营销模块: 积分、优惠券、分销、砍价、拼团、秒 多…

【转】[退役]纪念我的ACM——headacher@XDU

转自&#xff1a;http://hi.baidu.com/headacher/item/5a2ce1d50609091b20e25022 退役了&#xff0c;是时候总结一下我ACM的生涯了。虽然很舍不得&#xff0c;但这段回忆很值得纪念。ACM生涯虽然结束&#xff0c;但是新生活总要继续&#xff0c;还有很多东西需要我去学习&#…

VMware扩大硬盘后修改Linux逻辑卷大小

一、背景随着业务的不断成熟&#xff0c;数据库积累的数据也越来越多了。前些天发现服务器的磁盘将要满了。因此向虚拟化管理员申请增加磁盘空间。由于这个系统是建立在威睿的vSphere平台上的&#xff0c;因此虚拟化管理员只简单地通过 VMware vSphere Client 扩大了磁盘空间&a…