您的位置:首页 > 编程语言 > C语言/C++

C++反射机制的实现

2015-03-30 19:57 337 查看
.NET、C#这些语言都在内部实现了反射技术,因此用起来很是得心应手,但是C++则没有实现反射,因此只有自己写程序实现了。首先感谢网络上的师傅们,要我自己实现怕是很困难,毕竟我也是刚开始学习,网上的教程很多,但是有一些很依赖具体的程序环境,如gcc等,我觉得很不爽,因此都没有参考。我要感谢http://blog.csdn.net/wrq147/article/details/5603262 这个文章的作者,我是看他的程序实现了反射机制,然后在他上面的情况下进行了一点点修改。如果有不足的情况请大家见谅,大家一起学习学习哈。首先我贴出我整理好的这位原作者的代码。

代码如下 reflection.h

#ifndef _REFLECTION_H_
#define _REFLECTION_H_

#include <iostream>
#include <map>

typedef void* (*CreateFuntion)(void);
/*
以下为工厂类,用来创建类和注册类
*/
class ClassFactory
{
public:
static void* GetClassByName(std::string name) {
std::map<std::string,CreateFuntion>::const_iterator find;
find=m_clsMap.find(name);
if(find==m_clsMap.end()) {
return NULL;
}else {
return find->second();
}
}

static void RegistClass(std::string name, CreateFuntion method) {
m_clsMap.insert(std::make_pair(name, method));
}
private:
static std::map<std::string, CreateFuntion> m_clsMap;
};

std::map<std::string, CreateFuntion> ClassFactory::m_clsMap;

class RegistyClass {
public:
RegistyClass(std::string name,CreateFuntion method) {
ClassFactory::RegistClass(name,method);
}
};

template<class T,const char name[]>
class Register {
public:
Register() {
//这个一定要加,因为编译器不保证程序开始时就初始化变量rc
const RegistyClass tmp=rc;
}

static void* CreateInstance() {
return new T;
}
public:
static const RegistyClass rc;
};

template<class T,const char name[]>
const RegistyClass Register<T, name>::rc(name, Register<T, name>::CreateInstance);

#define DEFINE_CLASS(class_name) \
char NameArray##class_name[] = #class_name;\
class class_name:public Register<class_name, NameArray##class_name>

#define DEFINE_CLASS_EX(class_name, father_class) \
char NameArray##class_name[] = #class_name;\
class class_name:public Register<class_name, NameArray##class_name>,public father_class

#endif


先把代码贴出来,再解释(是我自己的理解),下面是cpp程序,非常简短。

main.cpp

DEFINE_CLASS(CG)
{
public:
void Display() {
printf("fff\n");
}
};

int _tmain(int argc, _TCHAR* argv[])
{
CG* tmp=(CG*)ClassFactory::GetClassByName("CG");
tmp->Display();

system("PAUSE");
return 0;
}



可以把代码直接写进文件编译后,应该是能够运行的,结果应该是打印 fff。

这是原作者的代码,分析如下:

工厂类ClassFactory有两个公共函数GetClassByName、RegistClass和一个私有成员map类型的m_clsMap,而且都是static类型,是类的方法,因此无需实例化即可调用。

m_clsMap:用于存储、读取键值对,索引为类的名字,值为一个函数指针,以便进行回调m_clsMap必须放在类外进行初始化。

GetClassByName函数:通过class的名字获得m_clsMap里面的函数指针。

RegistClass函数:用于向m_clsMap存入键值对<classname, CreateFuntion>,CreateFuntion是typdef的一个函数指针。

接下有一个类叫做RegistyClass,他就只有一个构造函数,构造函数用于向ClassFactory注册一个类名和一个回调函数。

接下来就是重点了:

class Register是一个模板类,他有一个CreateInstance方法,这个方法的作用就是返回该模板的一个实例化对象。同时Register类还有一个static const RegistyClass rc;的静态常量成员变量。由于是static const这种类型,因此rc需要在类外进行初始化。重点看下面这几段代码:

template<class T,const char name[]>
const RegistyClass Register<T, name>::rc(name, Register<T, name>::CreateInstance);

#define DEFINE_CLASS(class_name) \
char NameArray##class_name[] = #class_name;\
class class_name:public Register<class_name, NameArray##class_name>

#define DEFINE_CLASS_EX(class_name, father_class) \
char NameArray##class_name[] = #class_name;\
class class_name:public Register<class_name, NameArray##class_name>,public father_class


在宏里面,##表示将前后的宏参数相连,#表示将后面的参数变为一个字符串。

因此DEFINE_CLASS这个宏首先定义了一个char数组将class_name进行保存,并且让class_name继承public Register<class_name, NameArray##class_name>

这个时候的Register的模板参数已经确定,由于继承Register,class_name这个类也拥有了rc这个静态常量变量,也就会在运行之前进行初始化。此时rc的构造函数被调用,向ClassFactory的map进行了注册。因此Classfactory的GetClassByName函数可以找到我们注册的CreateInstance函数,而CreateInstance函数实现的正是返回class_name的一个实例化对象,因此实现了反射!。

DEFINE_CLASS和DEFINE_CLASS_EX函数的区别是多继承了一个father_class,这是C++独有的多重继承。以上就是原作者的代码和我自己的理解。下面是我对这段代码的改造。

为什么我需要改造?

第一,这个代码如果修改一下将CG如下定义:

class father {
public:
     virtual void Display() {};
};

DEFINE_CLASS_EX(CG, father) {
public:
 void Display() {
 printf("I am Here\n");
}
};


main函数不变化,将发现编译能够通过,但是运行就会出错,出错原因是map里面没有找到CG这个字符串。意思就是说DEFINE_CLASS_EX存在错误,经过我不断的对比和测试,我发现修改为一下代码就可以成功运行:

DEFINE_CLASS_EX(CG, father) {
public:
CG() {}   //显式声明构造函数
void Display() {
      printf("I am Here\n");
   }
};


即显式声明了CG的构造函数就可以成功运行了。

编译器什么时候隐式声明默认构造函数有两个条件:

1,该类没有显式声明任何构造函数。--既然你都定义了,系统就不给你生成了。

2,数据成员中没有const和reference。--因为要初始化。

满足则隐式生成默认构造函数。这里强调“任何”,是指即使用户自定义了复制构造函数或一个需要多个参数的构造函数,默认构造函数也不会被隐式声明了。

我在const RegistyClass Register<T, name>::rc(name, Register<T, name>::CreateInstance);处打了断点,发现如果没有CG的构造函数,不会执行这句话,如果显式声明了构造函数,则会执行这句话,这句话是向Classfactory注册的关键,因此问题出在这里。

由于CG继承了Register类,同时也继承了rc这个const变量,因此他没有默认构造函数。rc没有进行初始化的原因是因为他属于动态初始化(动态初始化是指调用了函数来进行初始化的一些全局或局部静态变量的初始化),因此编译器不保证程序开始时就初始化rc,没有初始化rc就没有向Classfactory注册。父类的Register类的构造函数中有一句
const RegistyClass tmp=rc;


这句话的目的应该是想保证初始化rc,而子类的构造函数在调用的时候会默认调用父类的构造函数,因此能够保证rc初始化。但是我还不清楚为何DEFINE_CLASS在CG没有显式声明构造函数也能成功的原因,究其DEFINE_CLASS和DEFINE_CLASS_EX的区别在于多重继承,因此我怀疑多重继承里面的原因导致了出错(求哪位大神给我说说啊,我查了半天都没找到为什么)。
DEFINE_CLASS


第二、由于是类,难免会继承其他类或者被继承,那么使用DEFINE_CLASS_EX就已经实现了多重继承,多重继承的几个好处和坏处这个文章有说明

http://bigasp.com/archives/486

由于多重继承存在很多容易出错的地方,因此我觉得只是为了实现反射而进行多重继承不太好,因此我决定最好取消多重继承,虽然代码要难看点,但是胜在安全一些(个人理解),由于上面的代码已经看懂了,我这里就直接贴上代码了。我取消了模板类Register。

reflection.h

#include <iostream>
#include <map>

typedef void* (*CreateFuntion)(void);
/*
以下为工厂类,用来创建类和注册类
*/
class ClassFactory
{
public:
static void* GetClassByName(std::string name) {
std::map<std::string,CreateFuntion>::const_iterator find;
find=m_clsMap.find(name);
if(find==m_clsMap.end()) {
return NULL;
}else {
return find->second();
}
}

static void RegistClass(std::string name, CreateFuntion method) {
m_clsMap.insert(std::make_pair(name, method));
}
private:
static std::map<std::string, CreateFuntion> m_clsMap;
};

std::map<std::string, CreateFuntion> ClassFactory::m_clsMap;

class RegistyClass {
public:
RegistyClass(std::string name,CreateFuntion method) {
ClassFactory::RegistClass(name,method);
}
};

#define DEFINE_CLASS(class_name) \
char NameArray##class_name[] = #class_name; \
class class_name

#define DEFINE_CLASS_EX(class_name, father_class) \
char NameArray##class_name[] = #class_name;\
class class_name:public father_class

#define DEFINE_RELFECT(class_name) \
public: \
static void* class_name##CreateInstance() { \
return new class_name;	\
}	\
static const RegistyClass class_name##rc;

#define DEFINE_RELFECT_INIT(class_name) \
const RegistyClass class_name##rc(NameArray##class_name, class_name::class_name##CreateInstance)

#endif


main.cpp如下:

class father {
public:
virtual void Display() {};
};

DEFINE_CLASS_EX(CG, father) {
public:
DEFINE_RELFECT(CG);
CG(){};
void Display() {
printf("I am Here\n");
}

};
DEFINE_RELFECT_INIT(CG);

int _tmain(int argc, _TCHAR* argv[])
{
CG* tmp=(CG*)ClassFactory::GetClassByName("CG");
tmp->Display();

system("PAUSE");

return 0;
}


运行结果:

肯定有不足之处,希望大家多多指正,我才刚刚学习C++,由于看了程杰的《大话设计模式》,发现抽象工厂模式一章节的反射技术只有在C#中有,因此才想到不断寻找C++的反射实现,希望大家指正,特别感谢上面实现该方式的原作者和《大话设计模式》的作者,谢谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: