上一篇提到如何去使用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的設計原理
有什麼問題記得要問彼得哥喔
讓我們一起大喊
彼得哥萬歲~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Aug 07 Thu 2008 23:43
RTTI 二部曲
close
全站熱搜
留言列表
發表留言