C++实现根据类名动态生成类对象

在开发后台服务的过程中,我们常常需要从数据库中取数据,并将数据缓存在本地中,另外,我们的服务还需要有更新数据的能力:包括定时的主动更新以及数据库数据更新时服务收到通知的被动更新。

之前在需要用到以上功能的时候,模仿着组内通用的数据Cache部分的代码来写,十分方便,基本上只需要自己写两个类:一个是取数据并缓存数据的类XXXData,一个是扇出数据的类XXXFetcher。

在需要使用数据的时候,通过:

FetcherFactory::getFetcher<XXXFetcher>()

即可获取一个XXXFetcher对象的指针。

查看FetcherFactory.h的备注:

/**
 * 配置中心, 存储各种配置, 会自动搜索项目中的Fetcher实现类, 加载, 并
 * 在收到刷新配置的通知时调用update方法
 * 需要在服务器启动的onInitialize中调用本类的Initialize方法进行初始化
 */

重点:会自动搜索项目中的Fetcher实现类并加载。

也就是说,FetcherFactory可以自动地查找项目中的Fetcher实现类,然后动态地创建XXXFethcer对象。

什么叫动态创建呢?

就是能够根据一个类的名字动态地创建该类的对象。

举个例子,你的程序需要用到一个文本文件,而此文件中存放着一些类的名字,你能根据这些名字动态的生成这些类吗?

/*****************************************************************************/
/*从文件中读出类的名字存放在字符串变量szClassName中,现在假设读出的字符串为 */
/* ”CString”,而类CString派生自CObject,那么我们可以用基类指针指向派生类 */
/***********************************/*****************************************/
char szClassName[20]
CObject *pOb;
pOb = new szClassName;  /* 概念版本 */

当然,以上只是一个概念版本,因为C++并不支持根据类名动态创建对象。C++中,操作符new后面只能跟数据结构的名称,不能跟字符串。

那么,有什么方式来达到“根据名字动态生成类对象”的目的吗?

最简单粗暴的方法就是:用嵌套的if…else…语句,反复比较读出的类名是否与预期的相同,然后生成此类的对象。的确,这是解决办法之一,但是如果程序要用到的类很多,而且经常会改变或添加,岂不是要经常改写识别字符串的函数?那样的话,会很繁琐且容易出错。

那么小组代码里的“动态创建”是如何实现的呢?

先看看Fetcher类的备注:

/**
 * 自动加载的逻辑类, 会在FetcherFactory::Initialize中被自动加载, 在FetcherFactory::update时循环调用所有Fetcher::update方法
 * 其实现类必须满足下述条件:
 * 1. 继承自本类
 * 2. 以Fetcher为类名结尾, 例如XXXFetcher, YYYFetcher
 * 3. 在.h和.cpp文件中分别添加DECLARE_FETCHERHOLDER_DYNCREATE和IMPLEMENT_FETCHERHOLDER_DYNCREATE宏
 * 4. 项目中不能有任何TC_DYN_Object子类同名
 *
 * 模拟MFC,动态生成类
 */

看来还是要先学习一下MFC动态生成类对象的方法。

下面我们简单模拟下MFC的实现方法。

基础知识:

class CExample
{
private:
int x;
public:
int y;
static long z;
… /*其它数据和函数*/
};

CExample j1,j2;

j1,j2两个对象共用一份变量z,就是说,如果一个成员变量被冠以static,那么无论这个类定义过多少对象,此成员变量在内存中始终只有一份。其实,变量z在类CExample还没有定义任何对象之前就已经在内存中实际存在了。我们可以利用这一特点来实现类的动态识别和生成。

在MFC中,有一个特殊的类CRuntimeClass,它是实现上述功能最关键的地方。

CRuntimeClass的声明:

class CObject;      /* 声明CObject是一个类,将在以后定义 */
struct CRuntimeClass
{
char *m_lpszClassName;  /* 存放类名 */
int m_nObjectSize;          /* 存放对象大小 */
CObject*    (*m_pfnCreateObject) (); /* 指向函数的指针,这个函数返回一个CObject  */
CRuntimeClass* m_pNextClass;    /* 类型的指针 */
static CRuntimeClass* pFirstClass;  /* 静态变量,链表头部 */
static CObject* CreateObject(const char* lpszClassName); /* 根据类名创建对象 */
};

接下来要的事情是:每个(我们想根据类名生成对象的)类都有一个静态的CRuntimeClass成员变量(因此每个类有且只有一份),并且该变量有一定的命名规则,比如下面例子中的classXXX。然后,通过链表将这些变量串起来,进而可以通过CRuntimeClass::pFirstClass来获取这些变量。

为了以后编程的方便性(同时也为了保密性),我们定义两个宏:

/* 声明 */
#define DECLARE_DYNAMIC(class_name)\
public://定义CRuntimeClass成员变量
static CRuntimeClass class##class_name;//获取CRuntimeClass变量的接口
virtual CRuntimeClass* GetRuntimeClass() const;
/* 实现 */
#define IMPLEMENT_DYNAMIC(class_name)\

//全局字符串,保存了本类的类名
char _lpsz##class_name[]=#class_name;\

//new一个本类的对象,非成员函数
CObject* Create##class_name()\
{ return new class_name; }
//初始化CRuntimeClass静态成员变量,将类名、上面的Create函数地址传进去
CRuntimeClass class_name::class##class_name={\
_lpsz##class_name,sizeof(class_name),Create##class_name};\

//定义一个CLASS_INIT类型的对象,使用CRuntimeClass成员变量的地址构造
static CLASS_INIT _init_##class_name(&class_name::class##class_name);\

//返回CRuntimeClass成员变量的地址
CRuntimeClass* class_name::GetRuntimeClass() const{ return &class_name::class##class_name; }

出现在宏定义中的##告诉编译器,把两个字符串捆绑在一起。

其中CLASS_INIT定义如下:

struct CLASS_INIT
{
    CLASS_INIT(CRuntimeClass* pNewClass);
};

CLASS_INIT 的构造函数实现如下:

CLASS_INIT::CLASS_INIT(CRuntimeClass* pNewClass)
{
    pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
    CRuntimeClass::pFirstClass = pNewClass;
}

于是各个类的CRuntimeClass成员变量都通过链表串联起来了,链表头为CRuntimeClass::pFirstClass(一个静态成员变量)。

这样,我们只要在类的定义文件(.h文件)中放入宏DECLARE_DYNAMIC,在类的实现文件(.cpp文件)中放入宏IMPLEMENT_DYNAMIC,就可以实现我们的目的。

举个例子:

//CObject.h
class CObject{
DECLARE_DYNAMIC(CObject)
}

//CObject.cpp
IMPLEMENT_DYNAMIC(CObject)

编译器做出来的是:

//CObject.h
class CObject{
public:
static CRuntimeClass classCObject;
virtual CRuntimeClass* GetRuntimeClass() const;
}

//CObject.cpp

char _lpszCObject[]=”CObject”;

CObject* CreateCObject()
{ return new CObject; }

CRuntimeClass CObject::classCObject={_lpszCObject,sizeof(class_name),CreateCObject};

static CLASS_INIT _init_CObject(&CObject::classCObject);

CRuntimeClass* CObject::GetRuntimeClass() const
{ return &CObject::classCObject; }

假设我们有3个类A,B,C,它们均继承自CObject,并分别添加了两个宏,当我们对代码进行编译之后,会发生什么事情呢?

  1. 为类CRuntimeClass创建一个静态变量pFirstClass,该变量为链表的头指针;
  2. 为类A、B、C各创建一个CRuntimeClass类型的静态变量;
  3. 每个类的.cpp文件中创建一个CLASS_INIT类型的静态变量,在该变量的构造函数中,将步骤2中类A、B、C的CRuntimeClass类型的变量插入到链表的头部。

这样子,只要我们分别在类的定义和声明中加入两个宏DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC,并让所有的类继承自CObject,便可得到一个链表,该链表保存了各个类中CRuntimeClass类型的变量,链表头部为CRuntimeClass::pFirstClass。

有了这个链表,我们便可以遍历整个链表,通过

CRuntimeClass::CreateObject(lpszClassName)

方法根据类名lpszClassName来创建相应的对象了~

于是我们就在不修改已经做好的代码的情况下,实现类对象的动态识别和生成的目的。以后,只要所有的类都派生自类CObject,并相应地加入两个宏,就具有了动态识别和生成的能力。

(以上代码比较简略,不一定十分准确,意在直观地理解、感受下这种实现的原理。)

通过学习,我们已经大致知道怎么用C++实现动态生成类对象的功能了。下一篇我将此基础之上,分析下本文开头讲到的数据Cache以及更新的相关功能代码。

时间: 2024-12-19 07:21:39

C++实现根据类名动态生成类对象的相关文章

构建施耐德楼控系统数据库后台服务器示例工程五(JAVA动态生成类)

在做数据库简易工具的过程中遇到了这么一个问题,即程序部署在tomcat下启动运行后,我们无法事先定义数据库中后续创建的表结构的Hibernate Bean对象,这样就需要我们在服务器运行起来后动态创建bean对象.cglib这个开源库即可解决我们的问题,动态创建JAVA对象. 1.cglib开源库简介 CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口. 反编译后CGLIB结构

通过字符串动态创建类对象

功能的需求是  不需要使用IOC 框架 来对创建指定对象. .NET 中  要想动态的 创建对象   做耦合  好像只能做到 将类生成DLL 后  然后 加载DLL 接着创建对象. JAVA 当个类文件 就可以运行. 希望做到  当读一个   .class  或者 .TXT 就可以  动态创建类对象  那多好啊!!! 还可以将 类文件 以字符串的形式写到 数据库中  通过数据库来 创建类对象. 然后就各种百度 谷歌 贴吧等  找有没相关的 解决方案    还好  找到了  . 不藏着 分享出来

Java逆向基础之动态生成类

为什么有这个东西,一方面时AOP框架的需要,另一方面是增加软件逆向的难度 动态生成类的技术目前大体上分为两类,一类是通过操作字节码框架如cglib/Javassist去实现,另一类就是JNI方式,调用dll/so库,内存中动态还原.这两种方式都能实现隐藏类 看一个Javassist动态生成类的例子 package com.vvvtimes; import java.lang.reflect.Modifier; import javassist.ClassPool; import javassis

设计模式之工厂模式:模拟DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC动态创建类对象

该形式的工厂模式是我项目中用到的方法,属于很成熟的模版,读者可以直接拿来在自己项目中使用.个人感觉这种方法真正做到了"开放封闭"的原则,最大好处是用户产品类的设计完全不依赖于该模式的实现,比如提供必须的相关函数等.如果不理解工厂模式的话,请参考网上其它文章,本实现在理解上有一点小小的难度.好东西,大家慢慢享用,话不多说,先放代码! 首先是产品基类,它相当于一个接口,产品需要有什么动作就写在这里吧! #ifndef _CPRODUCTBASE_H_ #define _CPRODUCTBA

JS 动态生成JSON对象

JS 动态生成JSON对象,一般用到JSON传递参数的时候,会用到. function onGeneratedRow(columnsResult) { var jsonData = {}; columnsResult.forEach(function(column) { var columnName = column.metadata.colName; jsonData[columnName] = column.value; }); viewData.employees.push(jsonDat

利用StringList对象来管理这些动态生成的对象

如果程序需要动态创建大量的对象,那么我们可以利用StringList对象来管理这些动态生成的对象.1.创建StringList对象:OBJ := TStringList.Create; 2.保存动态生成的对象:OBJ.AddObject('标识','对象名'); 3.调用生成的对象:(OBJ.Objects[序号/OBJ.IndexOf('标识')] as 对象类型).方法或属性或:对象类型(OBJ.Objects[序号/OBJ.IndexOf('标识')]).方法或属性 4.释放动态生成的对象

js动态创建类对象

1.定义函数,函数中定义类对象 f1=function(){ //定义类 function Pannel(name){ this.name = name; this.print = function(){ console.info(this.name); }; } //扩展类方法成员 Pannel.prototype.show = function(){ alert(this.name); } //返回类 return Pannel; } 2.执行函数,获得类 Pannel = f1(); 3.

mybatis-generator 动态生成实体对象、dao 以及相关的xml映射文件

.新建maven空项目 2.修改pom.xml文件 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mave

动态代理 -- 不用手动编写一个代理对象,不需要编写与目标对象相同的方法,在运行时内存中动态生成代理对象。

动态代理创建的核心代码: TargetInterface objProxy = (TargetInterface) Proxy.newProxyInstance( Target.class.getClassLoader(),//与目标对象相同的类加载器 new Class[]{TargetInterface.class}, new InvocationHandler() { @Override //proxy 即该代理对象 //method 代理对象的方法,里面封装这接口中的所有方法 //arg