blg-017-在中C++用全局对象的构造函数实现反射机制
在C++中利用全局对象的构造函数实现反射机制
- 在C++中利用全局对象的构造函数实现反射机制
- 1. 反射机制
- 2. 方法概述
- 3. 引入反射机制的简单工厂模式
- 3.1 工厂类
- 3.1.1 a_factory.h文件
- 3.1.2 a_factory.cpp
- 3.2 产品类
- 3.2.1 a_interface.h
- 3.2.2 a1 类
- 3.2.2.1 a1.h
- 3.2.2.1 a1.cpp
- 3.2.2.3 a2.h
- 3.2.2.4 a2.cpp
- 3.2.3 main.cpp
- 3.2.4 CMakeLists.txt
- 3.3 编译运行
- 3.3 如何编写新产品子类
- 3.3.1 a3.h
- 3.3.2 a3.cpp
- 3.3.3 CMakeLists.txt
- 3.3.4 编译运行
- 3.1 工厂类
- 4. 注意事项
1. 反射机制
Java语言中,对于任意一个类在运行状态下,都能够知道这个类的所有属性和方法;对于任意一个类的实例,也都能够调用它的任意方法和属性;这种动态获取类的信息以及动态调用对象方法的功能称为反射机制。
反射机制在我们日常的编程当中应用的不是很多,但是在设计模式中反射一个非常强大的工具。尤其是在设计模式中的工厂模式中,在工厂类中反射机制的使用可以免除switch case繁杂编写和维护。通过反射,可以方便的通过字符串指定类名从而创建出具体的产品类。但是在C++中,类的生成是要完完全全在编译时指定的。它无法像Java一样在运行时动态的创建对象。因此在增加产品类时将无可避免修改工厂类,这不是我们所希望看到的。一旦修改,就意味着要重新进行测试。
因此,为了使得C++更易于修改并且减少测试的工作量。本文将使用全局对象的构造函数来实现一定程度的反射机制。
2. 方法概述
由于C++语言的特性,任何对象的创建在编译期都必须进行明确。所以为了实现一定程度的反射,本文所述方法的主要结构就是在程序 Main 函数之前将所有的类的信息注册到一个数据结构中。这样在 Main 函数开始运行之后就可通过访问该数据结构获取全部类的信息。
由于C++是不允许在函数体外进行语句编写的,如何在 Main 函数运行之前构建好所有类的信息库是主要需要解决的问题。因此全局对象的构造函数恰好可以满足我们的需要。全局对象生命周期作用于整个程序的运行期,因此全局对象的内存分配和初始化都在程序正式运行之前。
3. 引入反射机制的简单工厂模式
3.1 工厂类
3.1.1 a_factory.h文件
#ifndef _A_FACTORY_
#define _A_FACTORY_#include "a_interface.h"#define REGISTER_TO_A_FACTORY(PRDT, PRDT_NAME) \PRDT reg_##PRDT; \a_factory fty_##PRDT(PRDT_NAME, (a_interface *)®_##PRDT);class a_factory
{public:a_factory(const char *name, a_interface *obj);public:static a_interface * get_a(const char *name);
};#endif
宏 REGISTER_TO_A_FACTORY 用于将产品类注册到该工厂中。从宏的定义中可以看出,对于每一个产品类申明了一个该产品类的全局对象。并将该全局产品对象的地址和产品名作为构造函数的参数又申明了一个全局工厂对象。该工厂对象负责该产品对象的注册。注册的具体实现见 3.1.2。
静态成员函数 get_a 用于通过指定注册的子类(产品)名从而获得子类对象(该产品)。
3.1.2 a_factory.cpp
#include "a_factory.h"
#include <iostream>
#include <cstring>using namespace std;static struct {char name[128] = {0};a_interface *ptr = NULL;
} registation[100];
static int reg_idx = 0;a_factory::a_factory(const char *name, a_interface *obj)
{string key(name);strcpy(registation[reg_idx].name, name);registation[reg_idx].ptr = obj;reg_idx++;
}a_interface * a_factory::get_a(const char *name)
{a_interface *tgt = NULL, *ret = NULL;int i = 0;for (i=0; i<reg_idx; i++) {if (strcasecmp(name, registation[i].name) == 0) {return (a_interface *)registation[i].ptr->clone();}}return NULL;
}
全局静态变量 registation 负责存储所有注册的对象,全局静态变量 reg_idx 记录注册的个数。本例中 registation 为长度为100的数组。故最多可以注册100个对象。
3.2 产品类
3.2.1 a_interface.h
#ifndef _A_INTERFACE_
#define _A_INTERFACE_class a_interface
{public:virtual void output() = 0;public:virtual a_interface * clone() = 0;
};#endif
output() 函数为主要的产品方法,子类通过重载后实现具体的产品功能。clone() 函数为反射机制中用于创建自身对象的方法。产品子类中都必须实现这两个函数。
3.2.2 a1 类
3.2.2.1 a1.h
#ifndef _A1_
#define _A1_#include "a_interface.h"class a1 : public a_interface
{public:virtual void output();protected:virtual a_interface * clone();
};#endif
子类(具体产品类)需要继承产品基类,并且实现其具体的产品方法,即output函数。
3.2.2.1 a1.cpp
#include <iostream>
#include "a1.h"
#include "a_factory.h"using namespace std;REGISTER_TO_A_FACTORY(a1, "a1");void a1::output()
{cout<<"This a1\n";
}a_interface *a1::clone()
{return (a_interface *) new a1();
}
子类只需在其CPP文件中使用REGISTER_TO_A_FACTORY宏,将自己注册到 registation 中即可。该注册过程通过全局对象的构造函数完成,其运行于 Main 函数之前。
3.2.2.3 a2.h
#ifndef _A2_
#define _A2_#include "a_interface.h"class a2 : public a_interface
{public:virtual void output();protected:virtual a_interface * clone();
};#endif
3.2.2.4 a2.cpp
#include <iostream>
#include "a2.h"
#include "a_factory.h"using namespace std;REGISTER_TO_A_FACTORY(a2, "a2");void a2::output()
{cout<<"This a2\n";
}a_interface * a2::clone()
{return (a_interface *) new a2();
}
3.2.3 main.cpp
#include <iostream>
#include "a_factory.h"using namespace std;int main(int argc, char **argv)
{a_interface *a = NULL;if (argc != 2) {cout<<"Input Error"<<endl;return 1;}a = a_factory::get_a(argv[1]);if (a == NULL) {cout<<"Not Found"<<endl;return 1;}a->output();return 0;
}
3.2.4 CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)PROJECT(ref)SET(DO_LIB_SRC ./a_factory.cpp./a1.cpp./a2.cpp)
SET(A_SRC ./main.cpp)SET(CMAKE_CXX_FLAGS_DEBUG -g)ADD_LIBRARY(${PROJECT_NAME} STATIC ${DO_LIB_SRC})
ADD_EXECUTABLE(a ${A_SRC})
TARGET_LINK_LIBRARIES(a -Wl,--whole-archive ${PROJECT_NAME} -Wl,--no-whole-archive)
3.3 编译运行
1.当前目录下文件
$ ls
a_factory.cpp a_interface.h a1.h a2.h main.cpp
a_factory.h a1.cpp a2.cpp CMakeLists.txt2.编译程序
$ mkdir build; cd build; cmake ../; make3.测试
$ ./a.exe a1
This a1
$ ./a.exe a2
This a2
$ ./a.exe a3
Not Found
通过上面的例子我们可以看到,只需通过字符串指定子类名,便可以创建出具体的子类。通过调用其继承并实现的虚函数实现其具体的产品方法。
3.3 如何编写新产品子类
在 3.2 小节中,子类产品 a2 的写法与 a1 类似。一个新的产品子类添加的步骤如下:
创建a3.h, a3.cpp文件
使 a3 类继承a_interface 类
实现产品函数和 clone 函数
在a3.cpp 文件中使用宏REGISTER_TO_A_FACTORY注册该产品子类
将a3.cpp 添加到CMakeLists.txt
由以上步骤可以看出,当需要新增子类时,只需编写子类代码即可,不用修改任何已有代码。
3.3.1 a3.h
#ifndef _A3_
#define _A3_#include "a_interface.h"class a3 : public a_interface
{public:virtual void output();public:virtual a_interface * clone();
};#endif
3.3.2 a3.cpp
#include <iostream>
#include "a_factory.h"
#include "a3.h"using namespace std;REGISTER_TO_A_FACTORY(a3, "a3");void a3::output()
{cout<<"This is a3\n";
}a_interface * a3::clone()
{return (a_interface *) new a3();
}
3.3.3 CMakeLists.txt
将:
SET(DO_LIB_SRC ./a_factory.cpp./a1.cpp./a2.cpp)
改为:
SET(DO_LIB_SRC ./a_factory.cpp./a1.cpp./a2.cpp./a3.cpp)
3.3.4 编译运行
1.当前目录下文件
$ ls
a_factory.cpp a_interface.h a1.h a2.h a3.h main.cpp
a_factory.h a1.cpp a2.cpp a3.cpp CMakeLists.txt2.编译程序
$ mkdir build; cd build; cmake ../; make3.测试
$ ./a.exe a1
This a1
$ ./a.exe a2
This a2
$ ./a.exe a3
This is a3
可以看到新增子产品类 a3 已经可以顺利运行。
4. 注意事项
如果将工厂类及其所有产品类生成一个静态库使用。则必须将整个静态库全部链接,否则将无法实现注册。因为所申明并定义的用于注册的全局对象并没有在外部使用,如果采用默认的链接方式,这些对象将不会存在于最终程序中。所以需要使用-Wl,–whole-archive参数。
blg-017-在中C++用全局对象的构造函数实现反射机制相关推荐
- 静态对象、全局对象与程序的运行机制
静态对象.全局对象与程序的运行机制 1. 在介绍静态对象.全局对象与程序的运行机制之间的关系之前,我们首先看一下atexit函数. atexit函数的声明为:int atexit( void ( ...
- java 反射获取对象_使用Java反射机制获取对象
本文由广州疯狂软件教育java培训分享: 构造接口Person,所有Person都会问好,但具体讲什么语言就不知道了! package interf; public interface Person ...
- java 泛型 与类反射_Java技能 —— 对象泛型以及类反射机制的应用
一,泛型和反射的初识 Java泛型是JDK 5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许程序员在编译时监测非法的类型.简单的理解就是参数化类型,我们一开始就知道这个泛型T是Str ...
- 2020-12-14(全局/静态对象的构造函数和析构函数调用的时机以及地址)
一般的对象实例化在什么时候实例化的呢? 是不是在main函数运行到那里的时候,然后创建对象,会调用类里面的构造函数. 那当我们遇到全局/静态对象的时候,它是不是也是需要在main函数里面慢慢构造呢? ...
- JavaScript 全局对象
JavaScript 全局对象 全局属性和函数可用于所有内建的 JavaScript 对象. 顶层函数(全局函数) 函数 描述 decodeURI() 解码某个编码的 URI. decodeURICo ...
- python获取网站window全局对象或方法的返回值
本章教程,主要介绍如何使用python获取网站中的window全局对象或方法的返回值. 目录 1.浏览器运行结果 2.程序代码 3.程序运行结果 1.浏览器运行结果 2.程序代码 #!/usr/bin ...
- java 定义全局对象_JavaScript 全局对象
全局对象描述 全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符.通过使用全局对象,可以访问所有其他所有预定义的对象.函数和属性.全局对象不是任何对象的属性,所以它没有名 ...
- Java中反射机制(Reflection)学习
Java语言的反射机制初步学习 首先看下基本概念: (一)在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任 意一个方法?答案是肯定的.这种动态获 ...
- Android APP热更新中的插件化(Hook技术:反射或动态代理),Demo (2)
修改AAPT,资源分区,用于Android插件化- https://github.com/BaoBaoJianqiang/AAPT -- Android下的挂钩(hook)和代码注入(inject) ...
最新文章
- 只知道TF和PyTorch还不够,快来看看怎么从PyTorch转向自动微分神器JAX
- 图像中添加二项式分布噪声
- Linux命令 iperf - 网络性能测试工具
- Boost:基于不同容器的有界缓冲区比较
- python的基本语术_Python中的基本语句详细资料说明
- SAP Spartacus初始化时和user token相关的APP INITIALIZER
- python for循环1 到10_python for循环(1)
- Activiti6 use spring-boot-starter-web meet requestMappingHandlerMapping error
- 大数据学习线路_[个人经验篇]大数据学习线路前导篇
- QT中使用全局变量在多个源程序中传递变量
- SharePoint 2013 托管导航及相关配置
- JavaScript-封装与继承(两种)
- MutationObserver监听页面是否加载完成
- 智能驾驶的深度神经网络模型嵌入式部署的线路思考
- yuv420和yuv420p的区别
- eclipse 如何查看Java源码
- python opencv实现 12色相环、24色相环(基于RGB空间和基于HSV空间实现)
- 【python算法】算法之线性增长与二次方增长小实验举例
- Emulex光纤卡lpfc配置文件的修改
- Webpack Chunk 分包规则