c++利用宏定义实现反射

反射是 java 的一个强大功能,我们往往有“根据类名创建该类实例的需求”,也就是说通过字符串就可以获得类实例。这种需求在 c++ 中也是存在的,下面分享一下在 c++ 中通过宏定义来实现类似功能的思路。

1、创建共同的基类。

我们需要为被“反射”的类,创建一个共同的基类。我们创建如下的 Base 基类, work() 方法主要是打印出不同内容,以和其他子类做区分。

基类 Base.h

#pragma once
#include 
#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 
#include 

class Base;
typedef Base* (*CREATER)();

class Factory
{
public:
	static Factory* getInstance();
	/// 
	/// 注册类到 Factory 中
	/// 
	/// 类名称
	/// 该类对应的静态构造方法
	void registerClass(const char *className, CREATER creater);

	/// 
	/// 通过类名创建类实例
	/// 
	/// 类的名称
	/// 该类的一个实例
	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 
#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 
#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 因为没有注册,所以就无法成功创建。
c++利用宏定义实现反射_第1张图片
需要详细代码,可以参考如下。ClassFactory

你可能感兴趣的:(c++,反射,宏定义)