zoukankan      html  css  js  c++  java
  • 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)<< "
    ";    
        cout << "pGrandSon->IsKindOf(RUNTIME_CLASS(Father)) "<< pGrandSon->IsKindof(&Father::classFather)<< "
    ";
        cout << "pGrandSon->IsKindOf(RUNTIME_CLASS(Son)) "<< pGrandSon->IsKindof(&Son::classSon) << "
    ";
        cout << "pGrandSon->IsKindOf(RUNTIME_CLASS(Daughter)) "<< pGrandSon->IsKindof(&Daughter::classDaughter) << "
    ";
        delete pGrandSon;
        getchar();
    }

    执行结果如下:

    参考资料:

    《深入浅出MFC》

    欢迎转载,但转载请著名出处:曾是土木人

  • 相关阅读:
    守护进程、互斥锁、生产者消费者模型
    实现并发编程的基础理论
    udp协议
    进程与进程池
    tcp协议产生-粘包问题的解决方案
    day21面向对象_类
    day16_面向过程编程与模块导入
    day15_函数递归_匿名函数_内置函数
    三、运算符(阶段二)
    二、(续)基础语法--常量、变量和注释(阶段二)
  • 原文地址:https://www.cnblogs.com/hongfei/p/3478376.html
Copyright © 2011-2022 走看看