动机
为了加深对RTTI的理解,尝试实现了一个RTTI系统。不过做的比较简陋,功能如下
- 1.只支持单继承体系
- 2.系统必须有个超级类,作为一切类的最上层父类
- 3.手动添加MY_RTTI和END_SUPER_DECLARE_CLASS宏
- 4.实现了动态造型(dynamic_cast)
介绍
typeID的识别利用虚函数实现,利用虚函数的性质可以让对象输出真正的类型标志,而不会被其声明的类型所改变。为了安全的造型,实现了一个继承链。在系统内,每个类的声明里会加上一个s_RTTINode,用于存储继承信息,包括本类名,父亲的继承信息和派生类等继承信息。这样继承的关系就被串联起来了。定义如下:
////继承链结构体定义/////
typedef struct tagRTTI_Tree{
const char *pClassName;//类名
struct tagRTTI_Tree *pFather;//父亲
struct tagRTTI_Tree* pChild;//第一个派生类
struct tagRTTI_Tree* pBrothers;//兄弟类(同一个父亲)
}RTTI_Tree;
// 单继承结构如下:
// A
// / \
// B C
// | / \
// D E F
//
另外为了快速向上造型,对整个树进行了索引。如E可以向上为A和C,但是不允许为B。
源码
#include<iostream>
#include<vector>
#include<string>
#include<hash_map>
using namespace std;
////继承链结构体定义/////
typedef struct tagRTTI_Tree{
const char *pClassName;//类名
struct tagRTTI_Tree *pFather;//父亲
struct tagRTTI_Tree* pChild;//第一个派生类
struct tagRTTI_Tree* pBrothers;//兄弟类(同一个父亲)
}RTTI_Tree;
///////////////////////////////////////////////////////
////////////RTTI实现
//////////////////////////////////////////////////////
//继承链节点索引
hash_map<string,RTTI_Tree*> g_RTTI_Map;
///构造RTTI ID
#define MY_RTTI(CLASS_NAME) \
public: \
static RTTI_Tree s_RTTINode; \
virtual const char * typeID(){return "DUCO|"#CLASS_NAME;}
//构造RTTI继承结构
#define END_DECLARE_CLASS(CLASS_NAME,F_CLASS_NAME) \
RTTI_Tree CLASS_NAME::s_RTTINode=RTTI_Create_Link<CLASS_NAME,F_CLASS_NAME>("DUCO|"#CLASS_NAME);
//构造RTTI继承结构
#define END_SUPER_DECLARE_CLASS(CLASS_NAME) \
RTTI_Tree CLASS_NAME::s_RTTINode=RTTI_Create_Link<CLASS_NAME>("DUCO|"#CLASS_NAME);
//构造继承链 超级父亲
template<class MT>
RTTI_Tree RTTI_Create_Link(const char *pName){
RTTI_Tree t;
t.pClassName=pName;
string str(pName);
g_RTTI_Map[str]=&(MT::s_RTTINode);
t.pFather=NULL;
t.pBrothers=NULL;
t.pChild=NULL;
return t;
}
//构造继承链 普通类
template<class MT,class FT>
RTTI_Tree RTTI_Create_Link(const char *pName){
RTTI_Tree t;
RTTI_Tree *pCur;
t.pClassName=pName;//类名
t.pFather=&(FT::s_RTTINode);//父亲类
t.pChild=NULL;
t.pBrothers=NULL;
//父亲信息更新
if(FT::s_RTTINode.pChild==NULL)//第一个孩子
FT::s_RTTINode.pChild=&(MT::s_RTTINode);
else{
pCur=FT::s_RTTINode.pChild;
while (pCur->pBrothers)//找到最后一个孩子
pCur=pCur->pBrothers;
pCur->pBrothers=&(MT::s_RTTINode);
}
//映射表,索引整棵树
string str(pName);
g_RTTI_Map[str]=&(MT::s_RTTINode);
return t;
}
///////////////////////////////////////////////////////
////////////dynamic_cast 实现
//////////////////////////////////////////////////////
template<class Type,class S>//待转化类和super类
Type *my_dynamic_cast(S *s){
const char *pMyID=s->typeID();//获取本类ID
string str(pMyID);
RTTI_Tree *pCurTree=g_RTTI_Map [str];
//可转化类型为自己真实类和其父辈类型
while(pCurTree){
if(strcmp(pCurTree->pClassName,Type::s_RTTINode.pClassName)==0)
return (Type*)(s);
pCurTree=pCurTree->pFather;
}
return NULL;
}
///////////////////////////////////////////////////////
////////////demo
//////////////////////////////////////////////////////
/// 说明 1.只支持单继承体系
/// 2.系统必须有个超级类,作为一切类的最上层父类
/// 3.手动添加MY_RTTI和END_SUPER_DECLARE_CLASS宏
/// 4.实现了动态造型
//Base 类
class Shape {
MY_RTTI(Shape);
private:
void priFun1(){};
};
END_SUPER_DECLARE_CLASS(Shape)
//Circle
class Circle:public Shape {
MY_RTTI(Circle);
private:
void priFun2(){};
};
END_DECLARE_CLASS(Circle,Shape)
//Rect
class Rect:public Shape {
MY_RTTI(Rect);
private:
void priFun2(){};
};
END_DECLARE_CLASS(Rect,Shape)
//Red-Rect
class RedRect:public Rect {
MY_RTTI(RedRect);
private:
void priFun2(){};
};
END_DECLARE_CLASS(RedRect,Rect)
//
// Shape
// / \
// Rect Circle
// |
// RedRect
//
int main(){
//获取真实类型
Shape *s1,*s2,*s3;
s1=new Shape();
s2=new Circle();
s3=new RedRect();
cout<<s1->typeID()<<endl;// DUCO|Shape
cout<<s2->typeID()<<endl;// DUCO|Circle
///动态造型
auto *t1=my_dynamic_cast<Circle,Shape>(s1);//实际内容为Shape想转为CirCle
cout<<(int*)t1<<endl;//NULL
auto *t2=my_dynamic_cast<Circle,Shape>(s2);//实际内容为CirCle想转为CirCle
cout<<(int*)t2<<endl;//SUCCESS
auto *t3=my_dynamic_cast<Rect,Shape>(s2);//实际内容为CirCle想转为Rect(兄弟)
cout<<(int*)t3<<endl;//NULL
auto *t4=my_dynamic_cast<Rect,Shape>(s3);//实际内容为RedRect想转为Rect(直系)
cout<<(int*)t4<<endl;//SUCCESS
auto *t5=my_dynamic_cast<Circle,Shape>(s3);//实际内容为RedRect想转为Circle(非直系)
cout<<(int*)t5<<endl;//NULL
getchar();
return 0;
}
后记
昨天晚上查了一些RTTI的资料,其实自己理解的还不够深刻。据说c++对象模型那本书有详细,可能会过短时间再看一下,把自己的实现改进一些,比如支持多继承等。
另外记录下在实现时遇到的困难。刚开始没有理清思路,若想获取一个类的真实信息,需要使用虚函数来实现。如果使用类的静态变量或者函数是没办法实现的,因为使用静态变量前提是我要知道类信息,而我本来的需求就是动态去获取类信息。所以c++的dynamic_cast依赖于虚表。我的实现虽然没有直接利用虚表,但是建立的继承链本质上是利用了虚表(用其内容构建索引)。
而继承链又是另一回事了,继承链可以认为是静态的。它存储的是类之间的关系,跟具体对象无关。所以最后实现是在类声明中嵌入一个静态变量。最后因为要初始化静态变量,我需要实现一个初始化函数,而这个函数的变量是类名,所以可以看到我这部分实现不得不使用模板函数:template<class MT>RTTI_Tree RTTI_Create_Link(const char *pName);