根据字符串自动构造对应类

问题的起因是,我在做一个demo,有一个对象基类,以及一堆派生出的子对象,比如球体、立方体之类的对象。还有一个对象管理类,用于存储场景中的所有对象。那么在初始化的时候,代码是这么写的:

class objectInfo
{
private:vector<object*> vecObjs;
public:void Init(){vector name ={"Sphere","Cube","Cube","Cone",};for (int i = 0; i < name.size(); i  ){object* obj = nullptr;if (name[i] == "Sphere"){obj = new Sphere();}else if (name[i] == "Cube"){obj = new Cube();}else if (name[i] == "Cone"){obj = new Cone();}if (obj){vecObjs.push_back(obj);}// ....}}
};

虽然是不会有什么问题,但感觉这么写真的很蠢。所以开始研究自动做这件事的方法,也就是说,只要调一次Create()即可,不需要像上面这样写一长串的if / else,否则代码会比较难维护。

现在,问题可以简化为,给定一个字符串,自动构造出对应的对象。

C 目前已经支持了RTTI技术,在已有一个类的实例的时候,可以获取类的名字(字符串形式的)。但这点似乎并不能派上用场,因为我们需要在得到实例之前去做从字符串到类的解析。据我所知,大部分C 框架都会定义很多宏,来完成代码生成的过程,所以在使用这些框架的时候,每次定义一个类,都要加一堆奇奇怪怪的大写字符。我用过的包含MFC,Qt和Unreal,但具体实现细节,我还没有深究过。

依照代码生成的思路,我以一个C 弱鸡的角度,思考如果我自己去做这么一个自动生成器,应该怎么实现。(也就是我的做法大概率不是非常好的,如果希望实际使用的话,最好借鉴一些成熟的解决方案

首先,要给每个类定义一个函数,比如对于类Sphere,应该有这么一个Create函数(当然,我们所有的讨论是基于类有继承关系,以下例子中类Sphere继承自object):

object* Create()
{return new Sphere();
}

这个函数也就是某个自动生成器最终调用的一个函数。首先它应该不能是类A成员函数,因为此时类A还没有实例化,是访问不到的。

为了根据字符串找到这个函数,我想到的是,可以存在一个字符串到函数的map里,它大概长这么个样子:

map<string, function<object*()>>

也就是说,我们在定义一个类后,还需要完成如下的操作:

(1) 生成一个对应的 Create()函数。

(2) 把这个函数加入到map里。

如果让宏来完成,那么Create函数的生成就是这样的:

#define CREATE(class_name) \object* Create() { return new class_name();};\

根据以上思路,又引入了一个单例的Helper类(可以理解为Factory)来封装一些东西,最终第一版是这样的:

#include 
#include 
#include 
#include 
#include 
using namespace std;
class object;#define REGISTER(class_name) \Helper::Inst()->Push(#class_name,[]()->object* \{ return new class_name;});\class Helper
{
private:map<string, function<object*()>> mapStr2Func;static Helper* helper;Helper() {}
public:static Helper* Inst(){if (!helper){helper = new Helper();}return helper;}object* Createobject(string name){if (mapStr2Func.find(name) != mapStr2Func.end())return mapStr2Func[name]();return nullptr;}void Push(string name, function<object*()> func) { mapStr2Func[name] = func; }
};class object
{
public:virtual void Print() = 0;const char* GetClassName(){return typeid(*this).name();}
};class Sphere : public object
{void Print() override { cout << GetClassName() << endl; }
};class Cube : public object
{void Print() override { cout << GetClassName() << endl; }
};class Cone : public object
{void Print() override { cout << GetClassName() << endl; }
};class objectInfo
{
private:vector<object*> vecObjs;
public:void Init(){vector name ={"Sphere","Cube","Cube","Cone",};for (int i = 0; i < name.size(); i  ){object* obj = Helper::Inst()->Createobject(name[i]);if (obj){vecObjs.push_back(obj);obj->Print();}  }}
};Helper* Helper::helper = nullptr;
int main()
{REGISTER(Sphere)REGISTER(Cube)REGISTER(Cone)objectInfo obj;obj.Init();system("pause");
}

以上代码最终打印的结果为:

但这个代码有一处我非常不满意的地方,这个REGISTER宏只能放在函数体里,比如这里的main里,而我希望的是能够放在类声明的旁边。因为自动生成的代码包含了把函数放入map的过程,而这样的操作在全局空间中是不允许的,我们最多只能在全局空间写一些变量的声明加初始化,比如:

int x = 0;

但无法做:

int x ;
x = 0; // forbidden!

纠结这个宏所在位置的原因在于:类的声明和宏放在一起易于维护。

就我个人经验而言,如果项目中有这么一个类base,我想继承它做一个新的功能类,那么我一定会先参照它已有的另一个子类的代码,看它是如何写的,或者更直接的,我会把它复制过来,把不必要的东西删掉,留下必要的。如果宏和类的声明放在一起,那么这个东西我是不会落下的,照葫芦画瓢改一遍都不会出错。但作为新手而言,我肯定很难想到,我要到另外一个看起来毫不相干的类里,添加一句宏。

作为改进,为了让以上操作(map的insert操作),能顺利在全局空间执行,我又构造了一个类Generator,利用它的构造过程偷偷完成了这一过程,也就是我可以在全局空间写:

Generator* generator = new Generator();

然后把那一堆逻辑放在Generator的构造函数里。反正以上行为就是穿了个马甲。

在头文件中定义变量其实是不符合规范的,为了稳妥可以把这个宏挪到对应的实现文件里,但出于个人强迫症,我希望只在头文件声明就足以。我用的vs 2017竟然可以编译过,但不确定其它编译器是否可行。(难道这是msvc编译器的特性?)

最终的代码如下:

reflect.h

#pragma once
class object;#include 
using namespace std;
#define REGISTER(class_name) \class class_name##Generator : public Generator{\public:class_name##Generator() { \Helper::Inst()->Push(#class_name, this);}\object* Create() {return new class_name();}};\class_name##Generator* class_name##Inst = new class_name##Generator();class Generator
{
public:virtual object* Create() = 0;
};class Helper
{
private:map mapStr2Generator;static Helper* helper;Helper() {}
public:static Helper* Inst(){if (!helper){helper = new Helper();}return helper;}object* Createobject(string name){if (mapStr2Generator.find(name) != mapStr2Generator.end())return mapStr2Generator[name]->Create();return nullptr;}void Push(string name, Generator* generator) { mapStr2Generator[name] = generator; }
};

test.h

#pragma once#include 
#include 
#include 
#include 
#include "reflect.h"class object
{
public:virtual void Print() = 0;const char* GetClassName(){return typeid(*this).name();}
};class Sphere : public object
{void Print() override { cout << GetClassName() << endl; }
};
REGISTER(Sphere)class Cube : public object
{void Print() override { cout << GetClassName() << endl; }
};
REGISTER(Cube)

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

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

相关文章

openshift k8s_带有DIY的Openshift上的Spring Boot / Java 8 / Tomcat 8

openshift k8sDIY盒带是一种实验性盒带&#xff0c;提供了一种在OpenShift上测试不受支持的语言的方法。 它提供了最小限度的自由形式的支架&#xff0c;将墨盒的所有细节留给了应用程序开发人员 。 这篇博客文章说明了结合了PostgreSQL服务的Spring Boot / Java 8 / Tomcat 8应…

都兰县第一中学计算机,都兰县第一中学教案.doc

PAGE \* MERGEFORMATPAGE \* MERGEFORMAT 1都兰县第一中学教案班级初一.班周次9时间45分钟课时2授课教师席得勋教学内容篮球&#xff1a;胸前双手传接球器 材篮球25个、栏架4个、垫子4个、长凳4个、标志桶4个教学目标运动参与目标:通过学习激发学生兴趣&#xff0c;使学生积极参…

.sql文件如何执行_mysql:一条SQL查询语句是如何执行的?

本篇文章将通过一条 SQL 的执行过程来介绍 MySQL 的基础架构。首先有一个 user_info 表&#xff0c;表里有一个 id 字段&#xff0c;执行下面这条查询语句&#xff1a;select * from user_info where id 1;返回结果为&#xff1a;-------------------------------------------…

jooq和jdbc_在jOOQ之上构建的RESTful JDBC HTTP服务器

jooq和jdbcjOOQ生态系统和社区正在持续增长。 我们个人总是很高兴看到基于jOOQ构建的其他开源项目。 今天&#xff0c;我们非常高兴为您介绍BjrnHarrtell结合REST和RDBMS的一种非常有趣的方法。 BjrnHarrtell从小就是瑞典的程序员。 他通常在Sweco Position AB上忙于编写GIS系…

C 虚函数表及多态内部原理详解

C 中的虚函数的作用主要是实现了多态的机制。关于多态&#xff0c;简而言之就是用父类型别的指针指向其子类的实例&#xff0c;然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”&#xff0c;这是一种泛型技术。虚函数表每个含有虚函数的类都…

html代码编辑器sp,在线HTML编译,文本关键字高亮显示,富文本编辑实现大概思路...

????最近被安排做了一个HTML在线编译功能&#xff0c;也利用这个机会对HTML在线编译&#xff0c;关键字高亮&#xff0c;富文本编辑器等的实现做了一些比较表面的研究&#xff0c;做简要记录&#xff0c;以便再次遇到作为参考。????在线HTML编译????首先需要一个能…

numpy 平方_NumPy入门指南

本文介绍了NumPy的基础知识&#xff0c;NumPy是使用Python进行科学计算的软件包。我们将在此处介绍几类基本的数组操作&#xff1a; 创建NumPy数组 重塑数组 NumPy的数学运算 数组的索引和切片 遍历数组首先&#xff0c;让我们将NumPy导入为np。 这使我们可以使用快捷方式np来引…

android 揭示动画_揭示垃圾收集暂停的时间长度

android 揭示动画有几种方法可以改善您的产品。 一种这样的方法是仔细跟踪用户的体验并在此基础上进行改进。 我们确实自己应用了此技术&#xff0c;并再次花了一些时间查看不同的数据 除了我们追求的许多其他方面之外&#xff0c;我们还提出了一个问题“延迟GC触发应用程序的…

10 张程序员喜爱的壁纸,需要自取~

喜欢的话就请点个再看&#xff0c;分享到朋友圈吧~

iptables 开放远程_JavaWeb项目的部署以及远程调试

不点蓝字&#xff0c;我们哪来故事&#xff1f;Linux环境下软件的安装Linux环境下的程序的安装、更新、卸载和查看。rpm 命令&#xff1a;相当于windows程序的添加/卸载程序&#xff0c;进程程序的安装&#xff0c;查看&#xff0c;卸载。本地程序安装&#xff1a;rpm -ivh 程序…

计算机网络应用基础论文,计算机网络应用基础概述论文

如今计算机网络技术应用的范围比较普遍&#xff0c;已经渗透到了人们工作和生活的各个方面。计算机网络的应用代表着社会进入了一个全新的时代&#xff0c;是生产力发展到一定阶段的产物。下面是答.案.网 ZQNf.Com小编给大家推荐的计算机网络应用基础概述论文&#xff0c;希望大…

java 性能调优_Java性能调优调查结果(第一部分)

java 性能调优我们在2014年10月进行了Java性能调优调查。该调查的主要目的是收集对Java性能世界的见解&#xff0c;以改善Plumbr产品。 但是&#xff0c;我们也很高兴与您分享有趣的结果。 我们收集的数据为进行冗长的分析提供了素材&#xff0c;因此我们决定将结果划分为一系列…

wps生成正态分布的随机数_量子计算与机器学习: 量子生成对抗网络QGAN

随着量子信息和量子计算的快速发展&#xff08;经费多了&#xff09;&#xff0c;科研工作者们一边感叹着量子计算机时代即将拥有的强大计算能力&#xff0c;一边又在考虑着如何将现有的高效算法和量子计算机相适配。作为最近几年如此火爆的机器学习&#xff0c;也就自然而然地…

通过反汇编来理解restrict关键字

一次难忘的面试经历多年前&#xff0c;一次互联网某厂实习生的面试题&#xff0c;题目的代码片段很简单&#xff0c;如下&#xff1a;1 #include 2 int main()3 {4 int *restrict pInt (int*)malloc(4);5 int *pNewInt pInt;6 return 0;7 } 12345678面试官问…

python车牌识别系统开源代码_汽车牌照识别系统【YOLO+MLP】

车牌识别系统可以自动检测并识别图像中的车辆牌照&#xff0c;其算法主要包括牌照定位、牌照分割、字符识别等步骤。本文将给出一种基于深度学习的车牌识别系统方案。要快速掌握开发人工智能系统的技能&#xff0c;推荐汇智网的 机器学习系列在线课程由于可以自动地从视频图像中…

幻灯片演示什么模式最好_清洁单元测试图案–演示幻灯片

幻灯片演示什么模式最好我有机会在2014年GDG DevFestKarlsruhe会议上谈论“清洁单元测试模式”。 感谢组织者邀请我&#xff0c;也感谢所有听我讲话的人。 如所承诺的&#xff0c;我为那些想看一下我在演讲中没有讲过的其他幻灯片的人分享了演示文稿&#xff1a; 清洁单元测试…

js text 和 html,JS DOM innerText和textContent的区别

innerText和textContent很多人会困惑&#xff0c;因为都可以用来获取文本内容&#xff0c;实际上&#xff0c;两者还是有很多区别的&#xff0c;本文就将介绍这两个属性的异同&#xff0c;希望可以对大家的学习有所帮助。一、之前错误的认识innerText IE6就开始支持&#xff0c…

如何用耳机翻页_游戏耳机的经典之作—罗技(G)Astro A40体验

前言经常去网吧玩游戏的朋友&#xff0c;在家玩如果没有头戴式耳机&#xff0c;那体验真的会差一大截。优秀的电竞游戏耳机会带来更好的游戏体验&#xff0c;不仅是舒适性。像绝地求生这类FPS游戏&#xff0c;游戏耳机会让听声辩位更容易更精确。最近因为大促降价入手的罗技&am…

我改了500个Bug,但是!!

IT程序猿 微博网友评论&#xff1a;空白一页blank&#xff1a;不写bug怎么改bug 抠Bee&#xff1a;不经意间病毒都出来了我在海的这边你在哪里丫&#xff1a;好了&#xff0c;发给开发小弟了富贵小小神仙&#xff1a;要不然呢&#xff0c;那些从开发到维护的程序员不都是靠这么…

jboss maven_使用Maven配置JBoss / Wildfly数据源

jboss maven大多数Java EE应用程序在其业务逻辑中使用数据库访问&#xff0c;因此开发人员经常面临在应用程序服务器中配置驱动程序和数据库连接属性的需求。 在本文中&#xff0c;我们将使用Maven为JBoss / Wildfly和Postgre数据库自动化该任务。 这项工作是根据我从以前的魔兽…