IxEngine开发笔记

导航

第三回 运行时类信息(Runtime Class Information)


刚开始学习MFC的时候,就碰到有关于RunTime Class Info的东西,可以在运行时得知一个对象的指针到底是什么类型的.当时并没有深入研究,用得也不多,只是觉得挺神奇的.而在这个engine的开发过程 中,才渐渐觉得这是个好东西了.我在engine里实现了一套这样的系统,用着还蛮顺手的,在这里介绍一下.
基本上这套系统(Runtime Class Information System,暂时简称为RtCIS吧)的功能有:
*.最重要的,就是可以为使用 RtCIS的类提供一个统一的内存分配器,所有这个类的实例全部从这个池中分配,而因为这个内存分配器是针对单一类型的数据的,所以可以写的比较简单而有 效率(使用模板),并且,可以用类的名字来标识这个内存分配池,这样可以方便内存池输出一些调试信息.
*.由于这个类的实例是分配在一个统一的内存池里的,所以可以方便快速的枚举出这个类的所有实例,这在有时候是很有用的.
*.可以根据类名(一个字符串)在运行时创建一个类的对象
*.可以在运行时来判断一个对象属于什么类型

这套系统的主要是通过在一个类中添加静态变量方法来实现的,下面列出一套精简版的实现,engine中的实现会复杂一些,包括一些其它的功能,比如记录类 的继承信息,枚举类的实例,为每个类分配一个ID,根据这个ID来创建实例等,这些功能全部可以在以下代码的基础上扩充,就不列举了.
*.首先定义一个类信息的基类:CClass
class CClass
{
public:
    virtual void *New()=0;
    virtual void Delete(void *)=0;
    const char*GetName()    {         return _classname.c_str();    }
    static void *NewByName(const char*clssname)
    {
        return _classes[std::string(clssname)]->New();
    }
protected:
    void SetName(const char *name)
    {
        _classname=name;
        _classes[_classname]=this;//将自己注册到全局的类信息表格中去
    }
    std::string _classname;//类的名字
    static std::map<std::string,CClass*> _classes;//全局的类信息表格
};
*.然后定义一个宏:DECLARE_CLASS(),用来在类里声明RtCIS,(
为清晰起见,省略了行末的\ )
#define DECLARE_CLASS(clss)
public:                                
    //类信息,派生自基类CClass
    class CClass_##clss:public CClass  
    {                                  
    public:                            
        virtual void *New()        {   return _pool.Alloc();  }    
        virtual void Delete(void *p) {  _pool.Free((clss*)p);        }
        CMemPool<clss> _pool;//这个类的内存池
    };                                                               
    static CClass_##clss *_instantiate()                     
    {                      
        static CClass_##clss instance;      
        instance.SetName(#clss); //设置类的名称,并注册到全局类信息表格中去
        return &instance;                                                      
    }
    CClass *GetClass(){return _class;}
    static CClass_##clss *_class;  //静态变量
*.然后再定义一个宏,用来实现RtCIS:
#define IMPLEMENT_CLASS(clss)
    clss::CClass_##clss * clss::_class=clss::_instantiate();//定义并初始化静态变量

*.再定义几个宏用来方便的使用这套系统:
#define Class_New(clss) (clss*)(clss::_class->New())
#define Class_Delete(p) (p)->GetClass()->Delete(p)
#define Class_NewByName(clssname) CClass::NewByName(clssname)
#define Class_GetName(p) (p)->GetClass()->GetName()
*.下面是一个例子,关于水果的:
在头文件里:
class CFruit
{
public:
    virtual CClass*GetClass()=0;
    //...
    //...
};
class CApple:public CFruit
{
public:
    DECLARE_CLASS(CApple);
    //...
    //...
};
class COrange:public CFruit
{
public:
    DECLARE_CLASS(COrange);
    //...
    //...
};
class CGrape:public CFruit
{
public:
    DECLARE_CLASS(CGrape);
    //...
    //...
};

在cpp文件里:
IMPLEMENT_CLASS(CApple);
IMPLEMENT_CLASS(COrange);
IMPLEMENT_CLASS(CGrape);
实际使用:
int main()
{
    CFruit* fruit1=Class_New(CApple);
    CFruit* fruit2=Class_New(COrange);
    CFruit* fruit3=Class_NewByName("CGrape");
    assert(std::string("CApple")==Class_GetName(fruit1));
    assert(std::string("COrange")==Class_GetName(fruit2));
    assert(std::string("CGrape")==Class_GetName(fruit3));

    Class_Delete(fruit1);
    Class_Delete(fruit2);
    Class_Delete(fruit3);
}

另外,这两天在看havok的物理引擎,它的对象分配也采用了类似的方法,并且有一个比较好的特性就是对于某一个类,可以在每一个线程里为它配备一个独立的内存分配器(这样就可以避免在内存分配代码里加锁,有助于提高效率),比如

void thread1()
{
    CFruit* fruit1=Class_New(CApple);
    Class_Delete(fruit1);
}

void thread2()
{
    CFruit* fruit2=Class_New(CApple);
    Class_Delete(fruit2);
}
fruit1和fruit2是自动从两个独立的内存池里分配出来的,我还没看havok是怎么实现的,不过应该不会太麻烦.希望将来有时间加这个feature.


posted on 2008-09-03 20:39  ixnehc  阅读(2176)  评论(2编辑  收藏  举报