反射是 java 的一个强大功能,我们往往有“根据类名创建该类实例的需求”,也就是说通过字符串就可以获得类实例。这种需求在 c++ 中也是存在的,下面分享一下在 c++ 中通过宏定义来实现类似功能的思路。
1、创建共同的基类。
我们需要为被“反射
”的类,创建一个共同的基类。我们创建如下的 Base
基类, work()
方法主要是打印出不同内容,以和其他子类做区分。
基类 Base.h
#pragma once
#include <stdio.h>
#include "Factory.h"
class Base {
public:
Base() {
printf("base class is created\n"); };
virtual ~Base() {
};
virtual void work() {
printf("base class is work\n"); }
};
2、 创建工厂类
先不着创建子类,我们有了基类后,就可以先实现一个工厂类 Factory
。核心方法就是 registerClass()
和 registerClass()
,前者向 Factory 注册类,后者则实现通过字符串来创建类实例。classPool 则保存注册的类名和其对应的构造方法。
Factory.h
#pragma once
#include <vector>
#include <utility>
class Base;
typedef Base* (*CREATER)();
class Factory
{
public:
static Factory* getInstance();
/// <summary>
/// 注册类到 Factory 中
/// </summary>
/// <param name="className">类名称</param>
/// <param name="creater">该类对应的静态构造方法</param>
void registerClass(const char *className, CREATER creater);
/// <summary>
/// 通过类名创建类实例
/// </summary>
/// <param name="className">类的名称</param>
/// <returns>该类的一个实例</returns>
Base* create(const char *className);
private:
// date
static Factory* sThis;
// 存储 <类名,类构造方法> 的 key-value 对
std::vector<std::pair<const char*, CREATER> > classPool;
// method
Factory() {
};
~Factory() {
};
};
Factory.cpp
#include "Factory.h"
Factory* Factory::sThis = nullptr;
Factory* Factory::getInstance() {
if (!sThis) {
sThis = new Factory();
}
return sThis;
}
void Factory::registerClass(const char *className, CREATER creator) {
for (auto it = classPool.begin(); it != classPool.end(); it++) {
if (strcmp(it->first, className) == 0) {
printf("%s is already regitered\n", className);
return;
}
}
printf("%s is regitered\n", className);
classPool.emplace_back(std::make_pair(className, creator));
}
Base* Factory::create(const char* className) {
for (auto it = classPool.begin(); it != classPool.end(); it++) {
if (strcmp(it->first, className) == 0) {
CREATER creater = it->second;
return (*creater)();
}
}
printf("%s has not been registered\n", className);
return nullptr;
}
3、完善 Base 类
有了 Factory 类之后,似乎已经大功告成了。但尚存些问题,比如调用 registerClass()
方法时,需要一个静态构造方法,每个子类都需要定义一次,似乎有点麻烦,考虑在基类里定义好,这样就不需要在子类里复杂的写一遍了,这个时候就该我们的宏定义出场了。下面是完善后的 Base
基类。
#pragma once
#include <stdio.h>
#include "Factory.h"
class Base {
public:
Base() {
printf("base class is created\n"); };
virtual ~Base() {
};
virtual void work() {
printf("base class is work\n"); }
};
#define REGISTER_CLASS(_class) \
static Base* create() {
\
return new _class(); \
} \
void register##_class() {
\
Factory::getInstance()->registerClass(#_class, create); \
}
#define DO_REGISTER_CLASS(_class) \
extern void register##_class(); \
register##_class();
REGISTER_CLASS
这个宏定义了两个方法,create()
就是我们替子类定义好的静态构造方法.。register##_class()
把类名转换为字符串,并向 Factory 注册。每个子类调用这个宏后,就能自动定义好这两个方法了。
DO_REGISTER_CLASS
这个宏,是真正的注册方法,需要在 main() 或者说项目里其他特殊的位置调用,进行类的注册。
4、看一下子类
Child1
Child1.h
#pragma once
#include "Base.h"
class Child1 : public Base
{
public:
Child1();
void work();
};
Child1.cpp
#include "Child1.h"
REGISTER_CLASS(Child1);
Child1::Child1() {
printf("child1 class is created\n");
}
void Child1::work() {
printf("child1 class is work\n");
}
在 Child1.cpp 中调用了 REGISTER_CLASS(Child1);
定义了前述两个方法。
类似地,定义了 Child2,就不再展示了。
5、main 方法及测试
#include <iostream>
#include "Base.h"
int main()
{
// 向 factory 注册类,调用定义在 Base 中的宏
DO_REGISTER_CLASS(Child1);
DO_REGISTER_CLASS(Child2);
DO_REGISTER_CLASS(Child2);
// 根据类名创建 Child1
Base* mBase = Factory::getInstance()->create("Child1");
mBase->work();
// 根据类名创建 Child2
mBase = Factory::getInstance()->create("Child2");
mBase->work();
// 根据类名,尝试创建 Child3,但并不存在 Child3
Factory::getInstance()->create("Child3");
}
下面是运行结果,可以看到,Child1 和 Child2 都可以通过类名成功创建,但 Child3 因为没有注册,所以就无法成功创建。
需要详细代码,可以参考如下。ClassFactory