分享
 
 
 

动态2:由类名创建对象

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

前言:同上文一样,本文源于对另一位朋友的问题的解答(参见帖子http://community.csdn.net/Expert/topic/3202/3202729.xml?temp=5.602664E-02

C++不是动态语言,所以没法从语言机制上实现类的动态创建,但这样的需求却有可能存在,一个类似的例子便是MFC中CWnd类的Create方法,其第一个参数为Window Class的名字,这就允许用户通过class的名字来创建相应的窗口。

要想实现这一点,必须有一个“管理中心”,用于登记类的名字,并且通过名字能够调用某个方法来创建相应的类。结合类工厂的设计思想,这里我们让一套继承体系中的基类作为“管理中心”,由它来维护所有派生类的必要信息,包括类名和工厂函数,这二者必须建立起映射关系,map是不错的选择。定义了一个派生类后,它就自动向基类进行注册,可是如何实现自动呢?先看看程序吧:

// 为编写方便,下面的代码都位于.cpp文件中

#ifdef _MSC_VER

#pragma warning(disable: 4786)

#endif

#include <iostream>

#include <map>

using namespace std;

class Base

{

public:

virtual void Print() // 测试用

{

cout << "This is Base" << endl;

}

protected:

typedef Base* (*ClassGen)(); // 声明函数指针

static void Register(const char* class_name, ClassGen class_gen) // 注册函数

{

class_set.insert(map<const char*, ClassGen>::value_type(class_name, class_gen));

}

public:

static Base* Create(const char* class_name) // 工厂函数

{

map<const char*, ClassGen>::iterator iter;

if((iter = class_set.find(class_name)) != class_set.end())

{

return ((*iter).second)();

}

return NULL;

}

protected:

static map<const char*, ClassGen> class_set; // 存储子类信息

};

map<const char*, Base::ClassGen> Base::class_set; // 静态成员变量定义

class Derived : public Base

{

public:

struct DerivedRegister // 辅助类,用于注册

{

DerivedRegister()

{ // 注册子类,虽然map能保证唯一,但仍只注册一次

static bool bRegistered = false;

if(!bRegistered)

{

Base::Register("Derived", Derived::Create); // 注册子类信息

bRegistered = true;

}

}

};

static Base* Create() // 工厂函数

{

return new Derived;

}

public:

virtual void Print() // 测试用

{

cout << "This is Derived" << endl;

}

};

static Derived::DerivedRegister Derived_for_registering; // 没有其它机制能够保证在全局空间调用注册函数,

// 不得已,定义一个全局变量来完成这项光荣的任务,看起来有点龌龊

int main()

{

Base* pDerived = Base::Create("Derived"); // 类名可以动态输入

if(pDerived) pDerived->Print(); // 创建成功调用虚函数

else cout << "Create error" << endl;

system("pause");

return 0;

}

上面这样实现,有的朋友可能觉得使用起来很麻烦,实际上我们用宏定义来改造一下就会非常漂亮,代码呆会儿就会看到。还是说说自动注册功能吧,首先,为什么要实现自动注册呢?这是为了最大程度上隐藏实现,实际上采用手动注册也可以,只不过我们得在主函数里一一调用每个类的注册函数。那我们是如何实现自动注册的呢?方法是将注册代码放到一个辅助类的构造函数中,然后定义一个该类的静态全局变量,这样构造函数便被调用了,:),缺点是多了一个额外的对象,除了用于注册外,它什么用也没有(最初我没有采用辅助类,而是直接在派生类的构造函数中注册,然后定义派生类的全局变量,这样明显会浪费空间,采用辅助类便将额外开销降低到了最小)。

下面让我们瞧瞧用宏改造后的模样:

#ifdef _MSC_VER

#pragma warning(disable: 4786)

#endif

#include <iostream>

#include <map>

using namespace std;

// 用于声明具有动态创建功能的基类

#define DECLARE_DYNCRT_BASE(base) public: typedef base* (*ClassGen)(); static void Register(const char* class_name, ClassGen class_gen) { class_set.insert(map<const char*, ClassGen>::value_type(class_name, class_gen)); } public: static base* Create(const char* class_name) { map<const char*, ClassGen>::iterator iter; if((iter = class_set.find(class_name)) != class_set.end()) { return ((*iter).second)(); } return NULL; } protected: static map<const char*, ClassGen> class_set;

// 用于实现基类

#define IMPLEMENT_DYNCRT_BASE(base) map<const char*, base::ClassGen> base::class_set;

// 用于声明一个能够被动态创建的类

#define DECLARE_DYNCRT_CLASS(derived, base) public: struct derived##Register { derived##Register() { static bool bRegistered = false; if(!bRegistered) { base::Register(#derived, Create); bRegistered = true; } } }; static base* Create() { return new derived; }

// 用于实现一个能够被动态创建的类

#define IMPLEMENT_DYNCRT_CLASS(derived) static derived::derived##Register derived##_for_registering;

// 测试

class Base

{

DECLARE_DYNCRT_BASE(Base) // 声明动态基类

DECLARE_DYNCRT_CLASS(Base, Base) // 基类自己也可以动态创建

public:

virtual void Print()

{

cout << "This is Base" << endl;

}

};

IMPLEMENT_DYNCRT_BASE(Base) // 实现动态基类

IMPLEMENT_DYNCRT_CLASS(Base) // 实现动态类

class Derived : public Base

{

DECLARE_DYNCRT_CLASS(Derived, Base) // 声明动态类

public:

virtual void Print()

{

cout << "This is Derived" << endl;

}

};

IMPLEMENT_DYNCRT_CLASS(Derived) // 实现动态类

int main()

{

Base* pBase = Base::Create("Base"); // 类名可以动态输入

if(pBase) pBase->Print(); // 创建成功调用虚函数

else cout << "Create Base error" << endl;

Base* pDerived = Base::Create("Derived"); // 类名可以动态输入

if(pDerived) pDerived->Print(); // 创建成功调用虚函数

else cout << "Create Derived error" << endl;

system("pause");

return 0;

}

上面的宏定义可以重复利用(是不是有点似曾相识,是的,MFC中在对序列化和动态创建等支持时用到了类似的宏),在使用的时候,只需简单的使用这四个宏,即可实现动态创建,感觉还不错吧,不过提醒一点,其中的两个IMPLEMENT宏都应该放在.cpp文件中。

这种方法还是存在一些缺点的,不过本文主要是提供一种思路,可以根据具体的情况再作相应变动。

(freefalcon于2004.09.19)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有