c++实现反射类

作者:小菜 更新时间:2025-02-27 点击数:
简介:.NET下的很多技术都是基于反射机制来实现的,反射让.NET平台下的语言变得得心应手。

最简单的,比如枚举类型,我们我可以很容易的获得一个枚举变量的数值以及其名称

【菜科解读】

.NET下的很多技术都是基于反射机制来实现的,反射让.NET平台下的语言变得得心应手。

最简单的,比如枚举类型,我们我可以很容易的获得一个枚举变量的数值以及其名称字符串。

可是,在C++中,枚举变量本质上和一个整形变量没有区别,我们很难获取一个枚举变量的名称字符串。

其实在C++中,我们可以通过宏来实现类似反射的机制。

在很多程序设计中,经常会遇到这样的需求,即可以通过类的名字得到对应类型的对象,尤其是一种数据需要很多策略处理的时候。

比如对于网页类型的识别,一篇网页可能是视频类型、新闻类型、图片类型、网站首页、百科等很多类型中的一种,网页类型对于搜索引擎来说是非常重要的,计算rank的时候网页类型往往是一个非常重要的因子。

具体实现的时候,网页类型识别的策略可以封装在类中,这样一个策略就可以设计成一个类。

但是后期随着对网页理解的越来越深入,就会出现以下两种情景: 需要添加新的网页类型,因此需要添加对应的类型识别类; 有些类型已经不再需要或者是进行了重新划分,那么需要删除掉这些类型或者是让这些类型识别模块不再生效。

这种应用场景下,添加或移除网页类型识别模块时,最好能够非常方便,并且不会影响到已有的程序。

一个比较好的方案是,定义一个类型识别的基类PageTypeDetector,每个类型识别策略都继承自这个基类。

比如需要一个新闻页识别的新策略,那么定义类NewsPageTypeDetector,该类继承PageTypeDetector。

在添加NewsPageTypeDetector到网页类型识别的主程序时,在配置文件中进行配置,添加NewsPageTypeDetector类,让该类生效,而主程序和其他类型识别策略的程序都不需要进行改动。

另外,如果不再需要图片网页类型识别,那么就把图片类型识别对应的类名直接从配置发文件中删除即可。

为了实现上述目标,我们需要从类名到类型的映射,可以称为反射。

因为配置文件中的信息在程序内部得到的都是纯字符串,程序需要根据字符串生成对应的识别类。

当然,这个在本身已包含反射机制的程序设计语言中很容易实现,比如JAVA,但是由于C++中语言本身不支持这种机制,因此,需要用其他的方法来模拟这种机制。

首先,我们从最简单的方式开始,定义一个工厂方法,该方法负责根据类名生成相应类的对象,函数定义可以如下: 1 PageTypeDetector* DetectorFactoryCreate(conststring& class_name); 生成新闻网页类型识别的类可以如下调用: 1 PageTypeDetector* news_page_detector = DetectorFactoryCreate("NewsPageTypeDetector"); DetectorFactoryCreate工厂方法中的实现逻辑大致是这样: 12345 if(class_name == "NewsDocTypeDetector") { returnnewNewsDocTypeDetector;} elseif(class_name == "...") { returnnew...;} 使用如上工厂方法创建类的方式具有非常明显的缺陷,每添加或删除一个新类,都需要修改工厂方法内的程序(添加if判断或者删除if判断,并且需要添加新类的头文件或者类声明),当然了,因为程序有了修改所以就需要重新编译(如果很多其他模块依赖该程序的话,重新编译也是一笔不小的开销)。

显然,这种方式虽然简单,但是极不易于维护。

这里,提出一个使用非常方便并且易于维护的解决方案,那就是使用宏。

虽然c++创始人Bjarne Stroustrup极力反对使用宏,但是在一些特定的场景中合理的使用宏会带来意想不到的效果。

首先,从使用宏最简单的一个实现开始,目标是可以通过类的名字得到相应的对象,因此应该有个方法类似于如下: 1 Any GetInstanceByName(conststring& class_name); 返回值为Any,因为不知道返回值究竟是什么类型,所以假定可以返回任何类型,这里的Any使用的是Boost中的Any。

该方法中需要new一个类型为class_name的对象返回,那么应该如何new该对象呢?借用上面使用工厂方法的经验,可以进一步使用工厂类,对于每个类,都有一个相应的工厂类ObjectFactoryClassName,由该工厂类负责生成相应的对象(为什么要使用工厂类?后面再作简单介绍)。

有了工厂类,也需要将类名与工厂类对应起来,对应方式可以使用map object_factory_map,object_factory_map负责从类名到相应工厂类的映射,这样,就可以通过类的名字找到对应ObjectFactory,然后使用ObjectFactory生成相应的对象。

但是如何将相应的工厂类添加到object_factory_map中去呢,我们需要在定义新类的时候就将对应的工厂类添加到object_factory_map中,这里需要一个函数负责添加工厂类到object_factory_map中去(为什么需要一个函数负责?最后作简单说明)。

负责将新类对应的工厂类添加到全局变量object_factory_map的函数必须在使用object_factory_map之前执行。

gcc中有一个关键字__attribute__((constructor)),使用该关键字声明的函数就可以在main函数之前执行。

到现在,程序的结构类似这样: 12345678910111213141516171819202122232425262728293031323334353637383940 // 负责实现反射的文件reflector.h:map object_factory_map;Any GetInstanceByName(conststring& name) {if(object_factory_map.find(name) != object_factory_map.end()) {returnobject_factory_map[name]->NewInstance();}returnNULL;}#define REFLECTOR(name) \classObjectFactory##name { \ public: \Any NewInstance() { \returnAny(newname); \} \}; \voidregister_factory_##name() { \if(object_factory_map.find(#name) == object_factory_map.end()) { \object_factory_map[#name] = newObjectFactory##name(); \} \} \__attribute__(constructor)voidregister_factory##name();// 调用文件test.ccclassTestClass {public:voidOut() {cout ();return0; } 到这里还有一个问题,全局变量ObjectFactoryMap是不能放在头文件中的,因为如果多个类包含该头文件时,就会出现重复定义的错误,是编译不过的。

因此,将该变量放在其源码reflector.cc文件中: 123456789101112 // reflector.h,包含声明:externmap object_factory_map;Any GetInstanceByName(conststring& name);// reflector.cc:map object_factory_map;Any GetInstanceByName(conststring& name) {if(object_factory_map.find(name) != object_factory_map.end()) {returnobject_factory_map[name]->NewInstance();}returnNULL;} 上述程序编译能够通过,但是运行时出错,后来定位到是在使用全局变量object_factory_map时出错,经过调试了很久,在网上查相应的资料也没找到。

经过不停的尝试,才发现原来是全局变量object_factory_map没有初始化,在仔细的测试了以后发现,是__attribute__((constructor))与全局变量类构造函数的执行顺序的问题,一般全局变量是在__attribute__(constructor)前完成初始化的,但是如果__attribute__是在main函数所在的文件,而全局变量是在其他文件定义的,那么__attribute__(constructor)就会在全局变量类构造函数前面执行,这样,上面的程序在全局变量类还没有完成初始化,也就是还没有执行构造函数,就在__attribute__(constructor)声明的函数中进行了使用,因此会出现问题。

不过,在执行__attribute__时已经看到了全局变量的定义,只是没有执行全局变量的构造函数(这里,如果全局变量不是类,而是普通类型,是没有问题的)。

所以,程序的结构还需要进一步修改。

现在解决如何定义和使用全局变量object_factory_map的问题。

既然我们不能直接使用该变量,那么可以通过显示调用函数来返回该变量,如果直接在函数中new一个对象返回的话,那么每次调用都会new一个新的对象,而我们全局只需要一个该对象,这时该是static出现的时候了。

我们可以这样定义: 12345 // reflector.ccmap& object_factory_map() {staticmap* factory_map = newmap;return*factory_map;} 这样定义还有另外一个优点,程序只是在真正需要调用g_objectfactory_map时才会生成相应的对象,而如果程序没有调用,也不会生成对应的对象。

当然,在这里new一个对象的代价不大,但是如果new的对象非常耗时的话,这种使用函数中static变量代替全局变量方法的优势就非常明显了。

到现在反射程序变成如下这样: 123456789101112131415161718192021222324252627282930313233343536373839404142 // 负责实现反射的文件reflector.h:// 工厂类的基类classObjectFactory {public:virtualAny NewInstance() {returnAny(); }};map& object_factory_map();Any GetInstanceByName(conststring #define REFLECTOR(name) \classObjectFactory##name : publicObjectFactory { \ public: \Any NewInstance() { \returnAny(newname); \} \}; \voidregister_factory_##name() { \if(object_factory_map().find(#name) == object_factory_map().end()) { \object_factory_map()[#name] = newObjectFactory##name(); \} \} \__attribute__(constructor)voidregister_factory##name()// reflector.ccmap& object_factory_map() {staticmap* factory_map = newmap;return*factory_map;}Any GetInstanceByName(conststring& name) {if(object_factory_map().find(name) != object_factory_map().end()) {returnobject_factory_map()[name]->NewInstance();}returnNULL;} 到现在接近尾声了,不过在很多时候,我们都是在已有基类的基础上添加新的类,就好比上述网页识别的程序,各个识别策略类都继承共同的基类,这样,我们可以进一步修改反射程序,将GetInstanceByName放在另外一个类中,返回的是基类的指针,因此在定义基类时也需要注册一个宏,如下所示,同时需要修改objector_factory_map的结构为map >,第一个key是基类的名字,第二map中的key是生成类的名字,基类宏的定义类似如下: 12345678910111213 #define REFLECTOR_BASE(base_class) \classbase_class##Reflector { \public: \staticbase_class* GetInstanceByName(conststring& name) { \map& map = object_factory_map()[#base_class]; \map::iterator iter = map.find(name); \if(iter == map.end()) { \returnNULL; \} \Any object = iter->second->NewInstance(); \return*(object.any_cast()); \} \}; 这里就不再详细讲修改后的代码了,有兴趣的朋友可以自己实现。

至于上面为什么需要使用工厂类,而不是直接new一个对应的对象返回,原因是直接new是不可以的。

例如如下定义: 1234 #define REFLECT(name) \Any GetInstanceByName(conststring& class_name) {returnAny(newname);} 如果是多个类使用的话,那么就会出现多个函数的定义。

如果也借助工厂类的实现,如下实现: 1234 #define REFLECT(name) \Any GetInstanceByName##name(conststring& class_name) {returnAny(newname);} 这样是不会出现重复定义了,但是这样在生产新的对象时需要指定特定的函数,这不又回到原点了吗?因此工厂类充当的是个中介的角色,我们可以保存工厂类,然后根据名称寻找特定的工厂类来生成对应的对象。

为什么需要使用函数添加工厂类?因为在程序中,全局空间中只能是变量的声明和定义,而不能是语句,例如: 可以这样写:int a = 10;int main() {}但是不能这样写:int a;a = 10;int main() {} 需要注意的知识点: 工厂模式; 全局变量的定义需要注意,不能定义在头文件中(当如,如果经过特殊处理,例如使用#ifndef保护另说); Any类型的实现;(准备写另外一篇文章来探讨其实现细节) 宏的定义以及使用;(基本覆盖了宏的所有知识) 全局变量构造函数与__attribute__((constructor))的执行顺序;(调试了很久) __attribute__((constructor))的问题;(编译器有关,放在函数定义前或定义后) 全局空间只能是声明或者定义,不能是语句; static在函数中的使用; 全局变量类的定义与使用。

c++,实现,反射,类,.NET,下,的,很多,技术,都是,

基于手机定位技术的位置服务应用研究与实现

专业的在线重装系统软件 全新设计 / 全新代码编写 / 全新支持所有机型 全新支持Window 11 安装 简介:随着移动互联网的迅速发展,基于手机定位技术的位置服务应用得到了广泛的应用。

本文将对基于手机定位技术的位置服务应用进行研究,并探讨其实现方法。

工具原料:系统版本:Android 12、iOS 15品牌型号:小米12、iPhone 13软件版本:高德地图v11.65.0.6194、百度地图v15.13.0一、手机定位技术概述手机定位技术是利用手机内置的GPS、基站、Wi-Fi等多种定位方式,获取手机的地理位置信息。

目前,常见的手机定位技术包括GPS定位、基站定位、Wi-Fi定位等。

其中,GPS定位精度最高,可以达到10米以内;基站定位精度相对较低,一般在100米左右;Wi-Fi定位精度介于两者之间,在50米左右。

二、位置服务应用的实现方法1、基于Android系统的位置服务应用实现在Android系统中,可以通过LocationManager类获取手机的位置信息。

首先需要在AndroidManifest.xml文件中添加ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION权限,然后通过getSystemService()方法获取LocationManager实例,再通过requestLocationUpdates()方法注册位置监听器,最后在onLocationChanged()回调方法中处理获取到的位置信息。

2、基于iOS系统的位置服务应用实现在iOS系统中,可以通过CoreLocation框架获取手机的位置信息。

首先需要在Info.plist文件中添加NSLocationWhenInUseUsageDescription或NSLocationAlwaysUsageDescription权限,然后通过CLLocationManager类获取位置管理器实例,再通过startUpdatingLocation()方法开始更新位置信息,最后在didUpdateLocations()回调方法中处理获取到的位置信息。

三、位置服务应用的应用场景1、导航应用导航应用是位置服务应用的典型代表,如高德地图、百度地图等。

通过获取用户的实时位置信息,结合地图数据和路况信息,为用户提供实时导航服务,引导用户到达目的地。

2、共享单车应用共享单车应用也是位置服务应用的一种,如哈啰单车、青桔单车等。

通过获取用户的位置信息,为用户显示附近的共享单车,方便用户快速找到并使用共享单车。

同时,也可以根据单车的位置信息,实现单车的调度和管理。

内容延伸:1、室内定位技术除了室外定位,位置服务应用还可以实现室内定位。

常见的室内定位技术包括Wi-Fi指纹定位、蓝牙定位、惯性导航定位等。

通过这些技术,可以实现在商场、博物馆等室内场景下的精确定位和导航服务。

2、位置服务与隐私保护位置服务应用在给用户带来便利的同时,也可能泄露用户的隐私信息。

因此,在开发位置服务应用时,需要重视用户的隐私保护,如让用户自主选择是否开启位置服务,对用户的位置信息进行脱敏处理等。

同时,也需要遵守相关的法律法规,保护用户的合法权益。

总结:基于手机定位技术的位置服务应用已经成为移动互联网时代的重要应用之一。

通过GPS、基站、Wi-Fi等多种定位技术,位置服务应用可以为用户提供导航、共享单车等多种服务,极大地便利了用户的生活。

然而,在开发位置服务应用时,也需要重视用户的隐私保护,遵守相关的法律法规。

相信随着技术的不断发展,位置服务应用将会有更广阔的应用前景。

手机数据镜像技术:实现数据备份与恢复的最佳选择

简介:手机数据镜像技术:实现数据备份与恢复的最佳选择手机数据的备份与恢复一直是用户关注的重要问题。

在日常使用中,我们经常会遇到手机数据丢失、误删或者手机损坏等情况,而这些数据对于我们来说可能是非常重要的。

因此,选择一种可靠的数据备份与恢复技术就显得尤为重要。

手机数据镜像技术就是一种非常好的选择。

工具原料:系统版本:iOS 14.5品牌型号:iPhone 12 Pro软件版本:iMazing 2.13.5一、手机数据备份与恢复的重要性手机中存储着我们的通讯录、短信、照片、视频等重要数据,一旦丢失就无法找回。

因此,及时备份手机数据是非常重要的。

手机数据镜像技术可以帮助我们快速备份手机中的所有数据,并且在需要时进行恢复,保证数据的安全性和完整性。

二、手机数据镜像技术的优势1、全面备份:手机数据镜像技术可以备份手机中的所有数据,包括通讯录、短信、照片、视频等,确保备份的完整性。

2、灵活恢复:手机数据镜像技术可以根据用户的需求选择性恢复数据,比如只恢复某个联系人的通讯录或者某个时间段的照片。

3、跨平台支持:手机数据镜像技术可以在不同的操作系统和手机品牌之间进行数据备份与恢复,非常方便。

三、手机数据镜像技术的操作步骤1、连接手机:将手机通过USB线连接到电脑上,并确保手机和电脑之间的连接正常。

2、打开软件:打开手机数据镜像软件,选择备份或恢复功能。

3、选择数据类型:根据需要选择要备份或恢复的数据类型,比如通讯录、短信、照片等。

4、开始操作:根据软件的提示进行操作,等待备份或恢复完成。

总结:手机数据镜像技术是实现数据备份与恢复的最佳选择。

它可以全面备份手机中的所有数据,并且灵活恢复所需数据。

同时,手机数据镜像技术还具有跨平台支持的优势,可以在不同的操作系统和手机品牌之间进行数据备份与恢复。

因此,我们可以放心地选择手机数据镜像技术来保护我们的手机数据。

加入收藏
               

c++实现反射类

点击下载文档

格式为doc格式

  • 账号登录
社交账号登录