C++:實現類似MFC的IsKindOf功能


假設需要一個類別庫,改類別庫共包含以下5個類:GrandFather(祖父類)、Father(父類)、Son(兒子類)、Daughter(女兒類)、GrandSon(孫子類)

各個類之間的繼承關系為:

 

相應的代碼為:

class GrandFather
{
};

class Father:public GrandFather
{
};

class Son:public Father
{
};

class Daughter:public Father
{
};

class GrandSon:public Son
{
};
View Code

想要讓該類別庫具備IsKindOf的功能,即能在執行時期偵測某個對象是否“屬於某個類別”,並傳回TRUE或FALSE。 

希望實現如下效果:

GrandFather* pGrandSon = new GrandSon;
cout << pGrandSon->IsKindof(GrandFather);    //應該輸出1
cout << pGrandSon->IsKindof(Father);        //應該輸出1
cout << pGrandSon->IsKindof(Son);            //應該輸出1
cout << pGrandSon->IsKindof(Daughter);        //應該輸出0

 設計思路:

pGrandSon->IsKindof(GrandFather)

為例,想要在運行時判斷:GrandSon 類是否是 GrandFather的子類。

我們知道,GrandSon類的繼承路線是這樣的:GrandSon--->Son--->Father--->GrandFather,因此GrandSon是GrandFather的子類。

所以我們在進行判斷的時候,需要從GrandSon類的父類進行找起,然后一級級的往上找,然后判斷某個父類是否為:GrandFather。

換句話說,想要判斷GrandSon是否是GrandFather的子類,需要“有跡可循”。

以上的“跡”,我們可以通過一個結構體:CRuntimeClass,來進行保存。

該結構體至少需要保存如下信息:類別名稱、串行的Next 指針,以及串行的First 指針。

First指針屬於全域變量,一份就好,所以應該是static變量。

struct CRuntimeClass
{
    LPCSTR m_lpszClassName;
    int m_nObjectSize;
    UINT m_wSchema;
    GrandFather* (PASCAL* m_pfnCreateObject)();
    CRuntimeClass* m_pBaseClass;
    static CRuntimeClass* pFirstClass;
    CRuntimeClass* m_pNextClass;
};

要實現IsKindOf()的功能,則類別庫中的5個類(GrandFather、Father、Son、Daughter、GrandSon)都應該各擁有一個CRuntimeClass成員變量。

進一步將類修改如下:

struct CRuntimeClass
{
    LPCSTR m_lpszClassName;
    static CRuntimeClass* pFirstClass;    
    CRuntimeClass* m_pNextClass;
    CRuntimeClass* m_pBaseClass;
};

class GrandFather
{
public:
    static CRuntimeClass classGrandFather;    
    virtual CRuntimeClass* GetRuntimeClass() const{return &GrandFather::classGrandFather;}
};

class Father:public GrandFather
{
public:
    static CRuntimeClass classFather;
    virtual CRuntimeClass* GetRuntimeClass() const{ return &Father::classFather;}
};

class Son:public Father
{
public:
    static CRuntimeClass classSon;
    virtual CRuntimeClass* GetRuntimeClass() const { return &Son::classSon; }
};

class Daughter:public Father
{
public:
    static CRuntimeClass classDaughter;
    virtual CRuntimeClass* GetRuntimeClass() const { return &Daughter::classDaughter; }
};

class GrandSon:public Son
{
public:
    static CRuntimeClass classGrandSon;
    virtual CRuntimeClass* GetRuntimeClass() const { return &GrandSon::classGrandSon; }
};
View Code

其中,GetRuntimeClass為內聯虛函數,作用為:獲取當前對象所屬類的 成員變量類型為CRuntimeClass的成員變量地址。

如:

CRuntimeClass* GrandSon::GetRuntimeClass() const 
{ 
    return &GrandSon::classGrandSon; 
}

返回成員變量classGrandSon的內存地址

接下來,需要完成最重要的一步:填充各個類中CRuntimeClass結構體的數據內容。

//祖父
static char szGrandFather[] = "GrandFather";
CRuntimeClass GrandFather::classGrandFather = { szGrandFather};

//父親
static char _lpszFather[] = "Father";
CRuntimeClass Father::classFather = {_lpszFather, NULL ,&GrandFather::classGrandFather};

//兒子
static char _lpszSon[] = "Son";
CRuntimeClass Son::classSon = {_lpszSon,NULL ,&Father::classFather };

//女兒
static char _lpszDaughter[] = "Daughter";
CRuntimeClass Daughter::classDaughter = {_lpszDaughter,NULL ,&Father::classFather};

//孫子
static char _lpszGrandSon[] = "GrandSon";
CRuntimeClass GrandSon::classGrandSon = {_lpszGrandSon, NULL ,&Son::classSon};
ClassFile.cpp

以上代碼將填充CRuntimeClass結構體的

LPCSTR m_lpszClassName(類名)、CRuntimeClass* m_pBaseClass(基類的CRuntimeClass結構地址)。

另外,還有2個成員變量需要進行初始化:pFirstClass和m_pNextClass

pFirstClass是靜態的,需要初始化為NULL:CRuntimeClass* CRuntimeClass::pFirstClass = NULL;

m_pNextClass的初始化操作,可以通過以下的結構體構造函數來完成:

在ClassFile.h頭文件中,增加如下代碼:

struct AFX_CLASSINIT
{ 
    AFX_CLASSINIT(CRuntimeClass* pNewClass)
    {
        pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
        CRuntimeClass::pFirstClass = pNewClass;
    }
};

以上代碼定義了一個結構體:AFX_CLASSINIT,並在結構體的構造函數中(C++中結構體也是有構造函數的……)對m_pNextClass 和 pFirstClass進行賦值。

在通初始化結構體的其他成員變量一樣,需要我們手動在ClassFile.cpp文件中添加初始化另外幾個成員變量的代碼:

//祖父
static AFX_CLASSINIT _init_GrandFather(&GrandFather::classGrandFather);
//父親
static AFX_CLASSINIT _init_Father(&Father::classFather);
//兒子
static AFX_CLASSINIT _init_Son(&Son::classSon);
//女兒
static AFX_CLASSINIT _init_Daughter(&Daughter::classDaughter);
//孫子
static AFX_CLASSINIT _init_GrandSon(&GrandSon::classGrandSon);

當執行以上代碼的時候,將把各個類中的CRuntimeClass結構體串聯起來。

這樣,經過以上的步驟,整個類別庫表就構造好了!類似下圖所示:

有了以上的類庫表之后,實現IsKindOf()函數的功能就輕而易舉了:

BOOL GrandFather::IsKindof(const CRuntimeClass* pClass)const
{
    CRuntimeClass* pClassThis = GetRuntimeClass();
    while(pClassThis != NULL)
    {
        if (pClassThis == pClass)
            return TRUE;
        pClassThis = pClassThis->m_pBaseClass;
    }
    return FALSE;
}

接着,就可以在main函數中進行調用了:

void main()
{
    GrandFather* pGrandSon = new GrandSon;
    cout << "pGrandSon->IsKindOf(RUNTIME_CLASS(GrandFather)) "<< pGrandSon->IsKindof(&GrandFather::classGrandFather)<< "\n";    
    cout << "pGrandSon->IsKindOf(RUNTIME_CLASS(Father)) "<< pGrandSon->IsKindof(&Father::classFather)<< "\n";
    cout << "pGrandSon->IsKindOf(RUNTIME_CLASS(Son)) "<< pGrandSon->IsKindof(&Son::classSon) << "\n";
    cout << "pGrandSon->IsKindOf(RUNTIME_CLASS(Daughter)) "<< pGrandSon->IsKindof(&Daughter::classDaughter) << "\n";
    delete pGrandSon;
    getchar();
}

執行結果如下:

參考資料:

《深入淺出MFC》

歡迎轉載,但轉載請著名出處:曾是土木人


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM