close

上一篇提到如何去使用C++函式庫所內建的RTTI操作方式,
但是怎麼自己設計一個還是有點霧煞煞是吧。
沒關係,奉彼德哥之命,我又看了一些書,爬了一些文章。
現在就來跟各位娓娓道來這些心路歷程。

要怎麼設計RTTI?
想想看,如果你要查字典,是不是要先知道部首,部首不知道,你還可以查注音,不會唸的話查筆畫總可以吧。
部首,注音,筆畫你可以想像成索引,索引到的地方就是字詞的解釋(好久沒查字典了,老實說,都快忘記怎麼查了)。
如此,當我們要建立RTTI的資料庫的時候,當類別被建構起來的時候紀錄必要的資訊。
怎麼樣,聽起來很簡單吧。
如果就這樣結束,我應該會被彼得哥的大鎖打死

來,首先我們需要一個資料結構來儲存類別型錄。
其中至少要有char *來表示類別名稱,一個Next指向下一個,一個First指向第一個,由於First只需要有一個,所以用static來修飾。

class CRTTIClass
{
public:
    char * m_pcClassName;
    int        m_iObjectSize;
    UINT  m_uiSchema;
    CObject * (PASCAL * m_pfnCreateObject) ();
    CRTTIClass * m_pBaseClass;
    static CRTTIClass * ms_pFirstClass;
    CRTTIClass * m_pNextClass;
}

記得要對ms_pFirstClass做初始化
CRTTIClass * CRTTIClass::ms_pFirstClass = NULL;

再來
為了要讓每一個class都能夠有這個CRTTIClass,間單的方式就是在每個class都有這個CRTTIClass的instance,但是這樣每個class都要再寫對每個CRTTIClass設定的動作。想一想,這真的是一件麻煩的事。

因此,我們使用巨集來完成這項工作
定義以下巨集
#define DECLARE_DYNAMIC(class_name) \
public:
    static CRTTIClass class##class_name;  \
    virtual CRTTIClass * GetRTTIClass() const;

如此你只要使用此巨集
    DECLARE_DYNAMIC(CA);
編譯器會幫你展開為
    public:
        static CRTTIClass classCA;
        virtual CRTTIClass * GetRTTIClass() const;

這樣還沒完
我們只完成宣告而已,設定與連結的動作當然也要神不知鬼不覺的做完才可以
再來定義一個巨集
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
    _IMPLEMENT_RTTICLASS(class_name, base_class_name, 0xFFFF, NULL)

#define _IMPLEMENT_RTTICLASS(class_name, base_class_name, wSchema, pfnNew) \
    static char _lpsz##class_name[] = #class_name; \
    CRTTIClass class_name::class##class_name = {\
        _lpsz##class_name, sizeof(class_name), wSchema, pfnNew, \
        RUNTIME_CLASS(base_class_name), NULL }; \
    static AFX_CLASSINT _int_##class_name(&class_name::class##class_name); \
    CRTTIClass * class_name::GetRTTIClass() const \
     { return &class_name::class##class_name; } \

在這裡又有一個RUNTIME_CLASS巨集,定義如下
#define RUNTIME_CLASS(class_name) \
    (&class_name::class##class_name)


其中更有一個重要的東西存在,就是AFX_CLASSINT這個struct,它負責linked list的串接工作
所以還必須宣告並定義AFX_CLASSINT
struct AFX_CLASSINT
{
    AFX_CLASSINT(CRTTIClass * pNewClass);
}

C++的struct可以有建構子與解構子

AFX_CLASSINT::AFX_CLASSINT(CRTTIClass * pNewClass)
{
    pNewClass->m_pNextClass = CRTTIClass::ms_pFirstClass;
    CRTTIClass::ms_pFirstClass = pNewClass;
}


接下來看一下範例
class CChild : public CBase
{
    //在類別內宣告
    DECLARE_DYNAMIC(CChild)
};

//在類別外宣告
IMPLEMENT_DYNAMIC(CChild, CBase)


上面的程式就會展開為
class CChild : public CBase
{
public:
    static CRTTIClass classCChild;
    virtual CRTTIClass * GetRTTIClass() const;
};

static char _lpszCChild[] = "CChild";
CRTTIClass CChild::classCChild = {
    _lpszCChild, sizeof(CChild), OxFFFF, NULL,
    &CBase::classCBase, NULL };
static AFX_CLASSINT _int_CChild(&CChild::classCChild);
CRTTIClass * CChild::GetRTTIClass() const
{ return &CChild::classCChild; }

如此複雜的工作就讓巨集給做完了,如此就不需要每個class都打一堆Code了
但是,串列的頭總是需要特別的處理。
class CBase
{
public:
    static CRTTIClass classCBase;
    virtual CRTTIClass * GetRTTIClass() const;
}
static char lpszCChild[] = "CBase";
struct CRTTIClass CBase::classCBase = 
{ lpszCChild, sizeof(CBase), 0xFFFF, NULL, NULL };
static AFX_CLASSINT _int_CBase(&CBase::classCBase);
CRTTIClass * CBase::GetRTTIClass() const
{
    return &CBase::classCBase;
}

聰明的你一定也看出來這些落落長的程式碼也可以用巨集來完成,如果你只有一個Root的class那你是可以不必設計一個專為root宣告RTTI的巨集,但是你如果想要很多種類別型錄,那麼設計一個給root使用的巨集是在所難免的,這就交給你們設計啦,希望彼得哥不會打我

如此類別型錄已經都設計好了,但總覺得少了什麼
對了!!!!!!
就是IsKindOf
有類別型錄但是沒有查表的方式,這要叫別人怎麼查阿
奉彼得哥之命,馬上補上IsKindOf

IsKindOf要怎麼設計呢?左思右想之下,想到一個偷懶的方法
請看以下範例
CBase
{
public:
    .....................
    .....................
    bool IsKindOf(const CRTTIClass * pClass) const;
};

bool CBase::IsKindOf(const CRTTIClass * pClass) const
{
    CRTTIClass * pClassThis = GetRTTIClass();
    while (pClassThis != NULL)
    {
        if (pClassThis = pClass)
            return true;
        pClassThis = pClassThis->m_pBaseClass;
    }
    return false;
}

IsKindOf就是再檢查某個物件是不是某一個物件的延伸,所以他追查的方式是同宗查詢
聰明的你一定也看出來它可以設計為巨集,並且設計在宣告root的RTTI巨集裡。

有些人會覺得這些使用的方式很眼熟,沒錯它就是MFC裡的RTTI
但這不是我們的主要探討所在,最重要的是讓一般人能夠理解RTTI的設計原理
有什麼問題記得要問彼得哥喔

讓我們一起大喊

彼得哥萬歲~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~









arrow
arrow
    全站熱搜
    創作者介紹
    創作者 kgsprogrammer 的頭像
    kgsprogrammer

    太陽系後援會

    SnakeEater 發表在 痞客邦 留言(0) 人氣()