|
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
文件中。
这种方法还是存在一些缺点的,不过本文主要是提供一种思路,可以根据具体的情况再作相应变动。
|