新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → 非议MFC[转帖] 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 4728 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 非议MFC[转帖] 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客楼主
    发贴心情 非议MFC[转帖]


    非议MFC(一)宏和类型定义的困惑  

    作者:cphj 时间:2001-10-06 09:58 出处:互联网 责编:My FAQ  

                  摘要:非议MFC(一)宏和类型定义的困惑


                              非议MFC(一)宏和类型定义的困惑

    关键字:C++,MFC,宏,macro,define,typedef

    有感于MFC库代码之去简就繁、之故弄玄虚,作下文,聊博一笑。


    请看一段常见的代码:

    //in user.h
    class CTest
    {
    private:
            int x;
    public:
            void SetX(int setX);
            int GetX() const;
            operator int *();
            operator const int *() const;
            void * GetSafeHandle() const;
    };

    //in user.cpp
    #include "user.h"
    void CTest::SetX(int setX)
    {
            x=setX;
    }
    //...

    我们(MFC的作者)认为这样的代码太浅显,不够深沉,缺少内涵,没有嚼劲。赶快include我们的头文件吧,它可以提高代码的整体形象。我们的口号是:让蓝色关键字在屏幕上消失。

    //in minimfc.h
    #define USER_CLASS class
    #define BEGIN_CLASS_DECLARATION {
    #define END_CLASS_DECLARATION };
    #define PRIVATE_MEMBER private:
    #define PROTECTED_MEMBER protected:
    #define PUBLIC_MEMBER public:
    #define OPERATOR_OVERLOAD operator
    #define CONSTANT_MEMBER_FUNTION const
    #define BEGIN_FUNCTION_DEFINITION {
    #define END_FUNCTION_DEFINITION }
    typedef void AFX_RETURN_VOID_FUNCTION;
    typedef int INT;
    typedef int * LPINT;
    typedef const int * LPCINT;
    typedef void * HOBJ;

    看!我们做到了。从此,我们的客户代码将这样写:

    //in user.h
    #include "minimfc.h"
    USER_CLASS CTest
    BEGIN_CLASS_DECLARATION
    PRIVATE_MEMBER
            INT x;
    PUBLIC_MEMBER
            AFX_RETURN_VOID_FUNCTION SetX(INT setX);
            INT GetX() CONSTANT_MEMBER_FUNTION;
            OPERATOR_OVERLOAD LPINT();
            OPERATOR_OVERLOAD LPCINT() CONSTANT_MEMBER_FUNTION;
            HOBJ GetSafeHandle() CONSTANT_MEMBER_FUNTION;
    END_CLASS_DECLARATION

    //in user.cpp
    #include "user.h"
    AFX_RETURN_VOID_FUNCTION CTest::SetX(INT setX)
    BEGIN_FUNCTION_DEFINITION
            x=setX;
    END_FUNCTION_DEFINITION
    //...


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/9/4 8:38:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客2
    发贴心情 
    非议MFC(二)逻辑上的不完备

    关键字:C++,MFC,RECT,CRect,POINT,CPoint,逻辑

    说明:程序片断仅包括理解所必需的代码,其余省略。

    1.设计缺失
    file://in <WINDEF.H>
    typedef struct tagRECT
    {
          LONG left;
          LONG top;
          LONG right;
          LONG bottom;
    } RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;
    file://in <AFXWIN.H>
    class CRect : public tagRECT
    {
          void SwapLeftRight();                  file://[1]

          BOOL IsRectEmpty() const;
          BOOL IsRectNull() const;
          void SetRectEmpty();                  file://[2]

          CRect(int l, int t, int r, int b);
          CRect(POINT topLeft, POINT bottomRight);
          CRect(POINT point, SIZE size);            file://[3]
          void SetRect(int x1, int y1, int x2, int y2);
          void SetRect(POINT topLeft, POINT bottomRight);
    };
    [1]为什么有SwapLeftRight(),却不提供对应的SwapTopBottom()?
    [2]同理,为什么不提供SetRectNull()呢?
    [3]既然三种方法都可以构造CRect对象,期望SetRect(POINT point, SIZE size)不是很合理吗?
    补上这些缺失的函数不过是举手之劳,“不因善小而不为”这句话不应该只挂在嘴上!

    2.前后不一致
    file://in <AFXWIN.H>
    class CPoint : public tagPOINT
    {
          CRect operator+(const RECT* lpRect) const;      file://[1]
    };
    typedef const RECT* LPCRECT;
    class CRect : public tagRECT
    {
          CRect operator+(LPCRECT lpRect) const;            file://[2]

          void operator+=(LPCRECT lpRect);            file://[3]
          void operator&=(const RECT& rect);            file://[4]
    };
    由于LPCRECT的类型定义放在中间,[1][2]的形参采取了形式不同但意义相同的声明方式。
    [3][4]是相似的运算符重载,却使用了不同的形参传递方式。
    每个人可以有自己的代码风格,但在同一个文件中,或者至少在同一个类中,总应该使用统一的风格吧!

    3.妨碍语法完整性
    file://in <AFXWIN.H>
    class CRect : public tagRECT
    {
          void operator=(const RECT& srcRect);
    };
    众所周知,在C和C++中,任何一个表达式的本身都是有值的,例如:a=100就是一个表达式,它的值是100。有了这个逻辑前提,链式表达式才能合理存在。在b=a=100;中,准确地说,是把a=100这个表达式的值100赋值给b。
    而CRect类中,赋值运算符的返回类型被错误地设定成void,于是CRect对象之间的赋值表达式没有了值,链式表达式也失效了。
    这个运算符的正确返回类型应该是CRect &。
    难道MFC的开发人员不看《Effective C++》?

    4.数学运算的对称破缺
    file://in <WINDEF.H>
    typedef struct tagPOINT
    {
        LONG x;
        LONG y;
    } POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
    file://in <AFXWIN.H>
    class CPoint : public tagPOINT
    {
          CPoint operator+(POINT point) const;
    };
    给出如下测试代码:
    POINT pt;
    CPoint pnt;
    CPoint result;
    result=pnt+pnt;            file://ok
    result=pt+pt;            file://error
    result=pnt+pt;            file://ok
    result=pt+pnt;            file://error
    pnt+pnt自然没问题,pt+pt报错也勉强可以理解,但是pnt+pt可以,pt+pnt偏偏就不行。直觉上,加法应该满足交换率,但是MFC“一鸣惊人”地打破了我们的思维惯性。
    实际上,如果把运算符函数声明为:
          friend const CPoint operator+(const POINT & pntL,const POINT & pntR);
    前述的四个语句就都可以通过了。
    也许有人会说:“friend关键字是非面向对象的,最好不要使用。”那么,我要说:首先,C++不是Java,它的主要设计原则是满足大型系统的效率、弹性和可维护性,面向对象中好的方法要采纳,非面向对象中好的方法也要采纳。其次,MFC在其他地方就使用了friend。
    如果认为pnt和pt之间不允许相加,那也应该把运算符函数声明为更安全的形式:
          const CPoint operator+(const CPoint & pntR) const;
    这样就只允许pnt和pnt相加了。
    要行都行,要不行都不行,不要歧视!

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/9/4 8:38:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客3
    发贴心情 
    非议MFC(三)库代码的质量问题

    关键字:C++,MFC,RECT,CRect,POINT,CPoint,质量

    说明:程序片断仅包括理解所必需的代码,其余省略。

    每个人的代码都不可能完全排除质量隐患,但MFC作为库代码,对其质量怎么苛求都不会过分。

    1.只顾效率
    file://in <WINDEF.H>
    typedef struct tagRECT
    {
          LONG left;
          LONG top;
          LONG right;
          LONG bottom;
    } RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;
    file://in <AFXWIN.H>
    class CRect : public tagRECT
    {
          CPoint& TopLeft();
          CPoint& BottomRight();
    };
    file://in <AFXWIN1.INL>
    _AFXWIN_INLINE CPoint& CRect::TopLeft()
          { return *((CPoint*)this); }            file://[1]
    _AFXWIN_INLINE CPoint& CRect::BottomRight()
          { return *((CPoint*)this+1); }            file://[2]
    TopLeft()通过返回CPoint &同时提供Set和Get功能,并且,返回CPoint &比返回CPoint效率高。但是,函数的实现必须依赖指针的跨越性转换(即从CRect *转换成完全不相干的CPoint *),另外,还要假设编译器是顺序存放各数据成员。随意转换指针类型,不安全;依赖编译器实现,不可移植;以后扩展时可维护性降低(如增加数据成员),还有可能导致错误(如引入虚函数时,有的编译器将虚表放在对象存储地址的前部)。

    2.不顾效率
    file://in <AFXWIN.H>
    class CRect : public tagRECT
    {
          BOOL PtInRect(POINT point) const;
    };
    因为POINT结构体大于32位地址长度,形参使用值传递效率不高,应该改为引用。
    软件的设计应该保持统一的取舍原则,如果说在上一点中,不惜采用那么极端的方式来提高效率,那么这里明显可以合理提高效率的地方为什么要放过呢?

    3.算法不严谨
    file://in <AFXWIN.H>
    class CRect : public tagRECT
    {
          BOOL IsRectEmpty() const;
    };
    IsRectEmpty()函数的功能是当矩形面积为空时返回1;当矩形面积为不空时返回0。
    给出如下测试代码:
    CRect rct(100,100,0,0);
    BOOL b=rct.IsRectEmpty();
    运行后b的值居然是1!?
    有些CRect的成员函数如:IntersectRect()、UnionRect()等只有先调用NormalizeRect()才能确保获得正确结果。但IsRectEmpty()完全没必要依赖NormalizeRect(),例如可以这样实现:
    BOOL CRect::IsRectEmpty() const
    {
          return (left==right&&up==bottom ? 1 : 0);
    }
    推测起来,MFC中的实现可能是:若矩形的right<=left或bottom<=up则返回1。

    4.无故破坏约定俗成的规则
    file://in <AFXWIN.H>
    class CRect : public tagRECT
    {
          void operator=(const RECT& srcRect);
          void operator+=(LPCRECT lpRect);
    };
    自定义类型不要毫无价值的与内建类型不兼容(《Effective C++》语)。operator=()应该返回CRect &,这样做还可以支持链式赋值。同理operator+=()也应该返回CRect &。

    5.没有尽力保证安全性
    file://in <AFXWIN.H>
    class CRect : public tagRECT
    {
          CRect operator+(LPCRECT lpRect) const;
    };
    operator+()应该返回const CRect,这样做可以禁止形如(a+b)=c;的病态语句,同时也保持了与内建类型的行为一致。

    6.没有尽力提高可用性和可靠性
    file://in <AFXWIN.H>
    class CDC : public CObject
    {
          BOOL BitBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC,
                int xSrc, int ySrc, DWORD dwRop);
    };
    做个简单的类比:
    file://in <STRING.H>
    size_t  __cdecl strlen(const char *);
    形参为什么要声明为const char *?因为,其一,const char *既可以接受常量字符串又可以接受非常量字符串,而char *只能接受非常量字符串。其二,const可以保证函数体不更改原字符串这一契约。
    所以BitBlt()的声明中,参数pSrcDC是原设备环境,不会改变,应该声明为const CDC *。

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/9/4 8:39:00
     
     gjymylover 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:1
      积分:61
      门派:XML.ORG.CN
      注册:2006/12/10

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给gjymylover发送一个短消息 把gjymylover加入好友 查看gjymylover的个人资料 搜索gjymylover在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看gjymylover的博客4
    发贴心情 
    未作深入研究 不看发表评论 XD
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/9/10 21:21:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/11/23 4:12:29

    本主题贴数4,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    78.125ms