vc++实现反射式数据库模版(1.运行时类识别)

前段时间写一个web应用,当然c#和java已经提供现成框架倒也方便,不过由于种种原因客户端需要MFC实现。在这里就介绍下怎么一步步完成这个功能。

遇到的最大问题就是实现众多类模型的增删改查。

分析:

对于类Person有这样定义:

class Person

{

public:

       string        name;//姓名

};

若实现对Person的增删改查,可以使用sql语句,例如实现增加:

//这里要求数据库中表名也是Person并具有与类成员相同名称的字段。

insert intoPerson (name) values (‘小明’);

 

//进一步如果我们直接用类的实例可以

//以下三行默认存在

Person p;

p.name  =  “小明”;

//可以用以下方式赋值(根据实际情况组合)

insert into Person(name) values (p.name);

 

如果说操作的类模型紧紧就一个Person或者几个已知的到轻松多了,但事实上总不这么如意。可能需要成百上千个类的模型,我们需要对所有类实例化成百上千次,针对每个类不同成员写各自的增删改查,后续开发及维护工作量明显很巨大,跟软件开发六大原则甚是违背。既然这样我们就需要对原有的代码进行修改。

 

分析insert into Person(name) values (‘小明’);

从左向右读,如果要完成这条语句,首先我们需要知道Person位置对应的表名(上面已经约束表名就等于我们类名),我们只能通过想法获取类名。这个可以用typeid(p).name()获取。接下来我们需要name位置对应的各种字段(我们同样约束字段名与类成员对应),我们也只能获取我们类中成员名及成员数量。然而c++早期版本并没有反射机制。我们遇到了目前最关键的问题。

仔细研究MFC发现MFC有序列化机制。就是把对象适当时候序列化成文字保存到文件中,需要的时候从文件中读取出来填充到成员中。MFC设计者也不知道需要序列化的类中的成员情况,经过分析我们就可以采用MFC序列化机制再次加工来实现我们的功能(由于我们自己的类有一定的特征要重新实现而不能直接使用MFC)。

由于MFC序列化基于MFC动态创建,他们又同时基于MFC运行时类识别,正好还可以替换掉上面的方式获取类名称(不替换也能做,会增加额外工作量)。

经上所述第一步我们就要模仿MFC实现运行时类识别。

MFC使用宏,我们也使用宏。名称做下修改,相同的简单讲,不同的地方做为重点讲。

   

    MFC:                  DECLARE_DYNAMIC(类名)              IMPLEMENT_DYNAMIC(类名,基类名)

我们:                TDDECLARE_DYNAMIC()                           TDIMPLEMENT_DYNCREATE(类名)

对比发现我们比MFC在初始化宏省略了类名,在实现宏省略了基类名,难道我们也能完成同样的功能?那是当然,这里先不解释,后面就会慢慢发现省去的妙处。

 

MFC:         #define DECLARE_DYNAMIC(class_name) \

public: \

        static constCRuntimeClass class##class_name; \

        virtual CRuntimeClass* GetRuntimeClass()const; \

 

我们:        #define TDDECLARE_DYNCREATE() \

public: \

    staticTDRuntimeClass runclass_name; \

    virtualTDRuntimeClass* GetRunTimeClass()const;\

    初始化宏我们把class#class_name换成runclass_name可以省略类名的传参。由于都是成员变量,后续对结果不会造成影响,因此是成立的。

 

    MFC:    #define IMPLEMENT_DYNAMIC(class_name,base_class_name) \

            IMPLEMENT_RUNTIMECLASS(class_name,base_class_name, 0xFFFF, NULL, NULL)

           

            #defineIMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew,class_init) \

    AFX_COMDAT const CRuntimeClassclass_name::class##class_name = { \

        #class_name, sizeof(class class_name), wSchema, pfnNew, \

            RUNTIME_CLASS(base_class_name), NULL, class_init }; \

    CRuntimeClass* class_name::GetRuntimeClass() const \

        { returnRUNTIME_CLASS(class_name); }

 

    我们:   #define TDIMPLEMENT_DYNCREATE(class_name) \

            TDRuntimeClassclass_name::runclass_name = {\

            #class_name };\

static TD_CLASSINIT_init_##class_name(&class_name::runclass_name);\

            TDRuntimeClass*class_name::GetRunTimeClass() const\

            {return &class_name::runclass_name;}\

    实现宏我们把MFC的两步合并成一步。由于我们解决问题只需要一层的继承,因此没必要保留基类,因此可以省略基类传参。

 

    可以看出MFC中有一个CRuntimeClass就是我们的TDRuntimeClass,进一步分析:

   

MFC:

struct CRuntimeClass

{

// Attributes

    LPCSTR m_lpszClassName;

    int m_nObjectSize;

    UINT m_wSchema; // schema number of theloaded class

    CObject* (PASCAL* m_pfnCreateObject)(); //NULL => abstract class

#ifdef _AFXDLL

    CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();

#else

    CRuntimeClass* m_pBaseClass;

#endif

 

// Operations

    CObject* CreateObject();

    BOOL IsDerivedFrom(constCRuntimeClass* pBaseClass)const;

 

    // dynamic name lookup and creation

    static CRuntimeClass* PASCALFromName(LPCSTR lpszClassName);

    static CRuntimeClass* PASCALFromName(LPCWSTR lpszClassName);

    static CObject* PASCALCreateObject(LPCSTR lpszClassName);

    static CObject* PASCALCreateObject(LPCWSTR lpszClassName);

 

//Implementation

    void Store(CArchive& ar) const;

    static CRuntimeClass* PASCALLoad(CArchive& ar, UINT* pwSchemaNum);

 

    // CRuntimeClass objects linkedtogether in simple list

    CRuntimeClass* m_pNextClass;      // linked list of registered classes

    const AFX_CLASSINIT*m_pClassInit;

};

 

我们:

struct TDRuntimeClass

{

    LPCSTR m_lpszClassName;

    static TDRuntimeClass*m_pFirstClass;    

    TDRuntimeClass* m_pNextClass;     

};

 

可以看出我们运行时类对MFC做了很大简化,仅保留了需要的成员。

m_lpszClassName保留了识别类的名称。而TDRuntimeClass又被我们需要的类模型定义。实现宏TDRuntimeClassclass_name::runclass_name = {#class_name }完成了对类模型初始化及对类名称的保存。因此使用时只需要通过类中的runclass_name中的m_lpszClassName就可以得到类的名称。

    从结构中可以看出TDRuntimeClass是个链表结构,m_pFirstClass是静态变量,这个静态变量保存了运行时结构的头指针,便于查找。m_pNextClass保存下一个结构。这样就像MFC一样把类的运行时结构串了起来,以后使用时方便从头依次匹配查找。

至于怎么串起来的要归功于实现宏中调用了的static TD_CLASSINIT _init_##class_name(&class_name::runclass_name)。下面我把TD_CLASSINIT代码贴下:

struct TD_CLASSINIT

{ TD_CLASSINIT(TDRuntimeClass* pNewClass) { TDClassInit(pNewClass); } };

在TDClassInit中实现了链表添加:

void TDClassInit(TDRuntimeClass*pNewClass)

{

  pNewClass->m_pNextClass =TDRuntimeClass::m_pFirstClass;

  TDRuntimeClass::m_pFirstClass =pNewClass;

}

 

至此仿写MFC的运行时类识别已经完成,程序初始化时就会自动创建起来这个链表,十分巧妙。下一节我们就要实现仿写MFC的动态创建。

原创作品欢迎转载QQ540880406

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值