C++动态识别与生成以及在MFC CRuntimeClass的应用

MFC 专栏收录该内容
24 篇文章 5 订阅
P>C++语言的爱好者们,不知你们注意到了没有,在编写某些程序时,如果能够根据一个类的名字动态地生成此类, 那么,整个程序的结构就能够非常简练.举个例子,你的程序需要用到一个文本文件,而此文件中存放着一些类的名 字,你能根据这些名字动态的生成这些类吗?让我们试着编写如下代码:

char szClassName[20]

/*****************************************************************************/

/*从文件中读出类的名字存放在字符串变量szClassName中,现在假设读出的字符 */

/*”CString”,而类CString派生自CObject,那么我们可以用基类指针指向派生类 */

/***********************************/*****************************************/

CObject *pOb;

pOb = new szClassName; /* 看出有问题了吧*/

这只是”语言版”动态生成,编译器是无论如何不肯正确编译的,操作符new后面只能数据结构的名称,不能跟字符串.看到这里,也许有的读者会说,我们可以用嵌套的if…else…语句,反复比较读出的类名是否与预期的相同,然后生成此类.的确,这是解决办法之一.但是,如果程序要用到的类很多,而且经常会改变或添加,岂不是要经常改写识别字符串的函数?那样的话,会很繁琐且容易出错.

熟悉VC++的MFC程序设计的人都知道,在MFC中,有一个特殊的类CRuntimeClass,其实这就是实现上述功能最关键的地方.通过分析MFC实现动态识别和生成的方法,发现其基本原理非常简单.现在,我们用最少的程序量,在DOS中把它模拟出来.

在正式介绍类CRuntimeClass的数据结构之前,先给C++语言的初学者讲述一个基本知识.请看如下代码:

class CExample

{

private:

int x;

public:

int y;

static long z;

… /*其它数据和函数*/

};

CExample j1,j2;

 

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

现在正式讲述类CRuntimeClass.请看:

(以下的变量?  有的仿照MFC的命名方式,其功用也跟MFC的差不多.)

class CObject; /*说明CObject是一个类,将在以后定义.*/

struct CRuntimeClass /*别惊讶,在C++语言中,struct和class差不多相同, */

{ /*只不过struct默认的是public,而class默认的 */

char *m_lpszClassName; /*是private,存储权限不同而已. */

int m_nObjectSize;

unsigned int m_wSchema;

CObject* (*m_lpfnConstruct) (); /*指向函数的指针,这个函数返回一个CObject */

CRuntimeClass* m_pNextClass; /* 类型的指针 */

static CRuntimeClass* pFirstClass;

static CObject* CreateObject(const char* lpszClassName);

};

我希望每个类都有一个CRuntimeClass成员变量(有且只有一份),并且有一定的命名规则.然后,利用一些手段将这些变量串联起来.

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

#define DECLARE_DYNAMIC(class_name)\ /*名称也有点相同*/

public:\

static CRuntimeClass class##class_name;\

virtual CRuntimeClass* GetRuntimeClass() const;

#define IMPLEMENT_DYNAMIC(class_name,wSchema)\

char _lpsz##class_name[]=#class_name;\

CObject* Create##class_name()\

{ return new class_name; }\

CRuntimeClass class_name::class##class_name={\

_lpsz##class_name,sizeof(class_name),wSchema,Create##class_name};\

static CLASS_INIT _init_##class_name(&class_name::class##class_name);\

CRuntimeClass* class_name::GetRuntimeClass() const\

{ return &class_name::class##class_name; }

出现在宏定义中的##告诉编译器,把两个字符串捆绑在一起.其中CLASS_INIT定义如下:

struct CLASS_INIT

{

CLASS_INIT(CRuntimeClass* pNewClass); /*是的,struct也可以有构造函数*/

};

这样,我们只要在类定义中放入宏DECLARE_DYNAMIC,在类的实现文件(就是.cpp文件)中放入宏IMPLEMENT_DYNAMIC,就可以神不知鬼不觉得实现我们的目的.

整个宏看起来有点吓人,其实没什么,替换而已.如果你这么使用宏:

DECLARE_DYNAMIC(CObject)

IMPLEMENT_DYNAMIC(CObject,0xFFFF)

编译器做出来的是:

public:

static CRuntimeClass classCObject;

virtual CRuntimeClass* GetRuntimeClass() const;

char _lpszCObject[]=”CObject”;

CObject* CreateCObject()

{ return new CObject; }

CRuntimeClass CObject::classCObject={

_lpszCObject,sizeof(class_name),0xFFFF,CreateCObject};

static CLASS_INIT _init_CObject(&CObject::classCObject);

CRuntimeClass* CObject::GetRuntimeClass() const

{ return &CObject::classCObject; }

整个宏看起来象是赋初值,其实不然,奥妙在于定义了一个结构CLASS_INIT ,其构造函数如下:

CLASS_INIT::CLASS_INIT(CRuntimeClass* pNewClass)

{

pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;

CRuntimeClass::pFirstClass = pNewClass;

}

这样,就把个各类的CRuntimeClass成员变量串联起来.让我们看一个实际例子.

class CObject /*这些代码分别定义在各个头文件中*/

{

private:

public:

DECLARE_DYNAMIC(CObject)

};

class CShape:public CObject

{

private:

public:

DECLARE_DYNAMIC(CShape)

};

class CRectangle:public CShape

{

private:

public:

DECLARE_DYNAMIC(CRectangle)

};

class CEllipse:public CShape

{

private:

public:

DECLARE_DYNAMIC(CEllipse)

};

/*以下的代码分别在各个类的实现文件中*/

CRuntimeClass * CRuntimeClass::pFirstClass; /*由于是static类型的变量,需在第一次 */

/*使用前说明一下 */

IMPLEMENT_DYNAMIC(CObject,0x0001)

IMPLEMENT_DYNAMIC(CShape,0x0002)

IMPLEMENT_DYNAMIC(CRectangle,0x0003)

IMPLEMENT_DYNAMIC(CEllipse,0x0004)

 

最后说明一下在类中的CreateObject函数,这个函数其实很简单,就是根据上面的链表,一个一个比较类名是否相同,如果相同则调用相应的构造函数动态生成此类,返回指向此类的指针(当然是利用基类指针指向派生类).这样,我们就利用两个宏DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC,在不修改已经做好的代码的情况下,实现了我们动态识别和生成的目的.以后,只要所有的类都派生自类CObject,就具有了动态识别和生成的能力.

上面的这些技巧都是从MFC中学到的,各位如果有兴趣的话,可以看一下MFC的源代码,在里面有更富技巧性的做法,当然也更复杂.

  • 3
    点赞
  • 1
    评论
  • 4
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 1 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

一个早起的程序员

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值