随笔 - 224  文章 - 41  trackbacks - 0
<2010年6月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

享受编程

常用链接

留言簿(11)

随笔分类(159)

随笔档案(224)

文章分类(2)

文章档案(4)

经典c++博客

搜索

  •  

最新评论

阅读排行榜

评论排行榜

WTL/ATL的分析

故事将往事尘封,而所有的事情都会回到最原始的状态来。

就像wtl那样抛弃了所谓的框架,放弃了大部分虚函数花哨的写法,而回归到了最原始的状态来,想不说它好它坏,一个东西的存在就有他的道理。

如果所不用mfc进行窗体的开发,而是自己搭建一个应用程序的话,估计写个小程序还行,但是在这种商业环境下估计并不适合。而网上也存在了很多用牛人自己写的一些框架结构,但是现在用的人都不多。

Wtl/atl是微软自己的东西,之前并没有完整的说明文档。Wtl是简单的,并没有mfc那么的复杂,个人感觉如果非常熟悉mfc的话,再来看wtl的内部结构,也可以感到wtl中的鬼斧神工之处。

关于wtl的学习资料:wtl_for_mfc_programmers,这本介绍了wtl的使用。如果要知道wtl的内部结构可以参考下面网站:

http://tech.ccidnet.com/art/1081/20021111/30384_1.html

Wtl的大部分类都是这种写法。

template< class T, class TBase = CButton, class TWinTraits = CControlWinTraits >

class ATL_NO_VTABLE CFontButtonImpl: 

public CWindowImpl< T, TBase, TWinTraits >,

public COwnerDraw< T >

{

...

wtl_for_mfc_programmers中对这段写法有一点说明。

如果你想要了解它是如何工作地,请看下面的例子:

template <class T>

class B1

{

public: 

    void SayHi() 

    {

       T* pT = static_cast<T*>(this);   // HUH?? 我将在下面解释

 

        pT->PrintClassName();

    }

protected:

    void PrintClassName() { cout << "This is B1"; }

};

 

class D1 : public B1<D1>

{

    // No overridden functions at all

};

 

class D2 : public B1<D2>

{

protected:

    void PrintClassName() { cout << "This is D2"; }

};

 

main()

{

    D1 d1;

    D2 d2;

 

    d1.SayHi();    // prints "This is B1"

    d2.SayHi();    // prints "This is D2"

}

这句代码static_cast<T*>(this) 就是窍门所在。它根据函数调用时的特殊处理将指向B1类型的指针this指派为D1D2类型的指针,因为模板代码是在编译其间生成的,所以只要编译器生成正确的继承列表,这样指派就是安全的。(如果你写成:

class D3 : public B1<D2>

就会有麻烦之所以安全是因为this对象只可能是指向D1D2(在某些情况下)类型的对象,不会是其他的东西。注意这很像C++的多态性(polymorphism),只是SayHi()方法不是虚函数。

要解释这是如何工作的,首先看对每个SayHi()函数的调用,在第一个函数调用,对象B1被指派为D1,所以代码被解释成:

void B1<D1>::SayHi()

{

    D1* pT = static_cast<D1*>(this);

 

    pT->PrintClassName();

}

由于D1没有重载PrintClassName(),所以查看基类B1B1PrintClassName(),所以B1PrintClassName()被调用。

现在看第二个函数调用SayHi(),这一次对象被指派为D2类型,SayHi()被解释成:

void B1<D2>::SayHi()

{

    D2* pT = static_cast<D2*>(this);

 

    pT->PrintClassName();

}

这一次,D2含有PrintClassName()方法,所以D2PrintClassName()方法被调用。

这种技术的有利之处在于:

不需要使用指向对象的指针。

节省内存,因为不需要虚函数表。

因为没有虚函数表所以不会发生在运行时调用空指针指向的虚函数。

所有的函数调用在编译时确定(译者加:区别于C++的虚函数机制使用的动态编连),有利于编译程序对代码的优化。

节省虚函数表在这个例子中看起来无足轻重(每个虚函数只有4个字节),但是设想一下如果有15个基类,每个类含有20个方法,加起来就相当可观了。

让我们回到上面来,要自定义自己的按钮,需要public COwnerDraw< T >

这个东西,看看这个东西到底是什么。

template <class T>

class COwnerDraw

{

public:

#if (_ATL_VER < 0x0700)

BOOL m_bHandledOD;

BOOL IsMsgHandled() const

{

return m_bHandledOD;

}

void SetMsgHandled(BOOL bHandled)

{

m_bHandledOD = bHandled;

}

#endif //(_ATL_VER < 0x0700)

// Message map and handlers

BEGIN_MSG_MAP(COwnerDraw< T >)

MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)

MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem)

MESSAGE_HANDLER(WM_COMPAREITEM, OnCompareItem)

MESSAGE_HANDLER(WM_DELETEITEM, OnDeleteItem)

ALT_MSG_MAP(1)

MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)

MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem)

MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem)

MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem)

END_MSG_MAP()

LRESULT OnDrawItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)

{

T* pT = static_cast<T*>(this);

pT->SetMsgHandled(TRUE);

pT->DrawItem((LPDRAWITEMSTRUCT)lParam);

bHandled = pT->IsMsgHandled();

return (LRESULT)TRUE;

}

LRESULT OnMeasureItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)

{

T* pT = static_cast<T*>(this);

pT->SetMsgHandled(TRUE);

pT->MeasureItem((LPMEASUREITEMSTRUCT)lParam);

bHandled = pT->IsMsgHandled();

return (LRESULT)TRUE;

}

LRESULT OnCompareItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)

{

T* pT = static_cast<T*>(this);

pT->SetMsgHandled(TRUE);

bHandled = pT->IsMsgHandled();

return (LRESULT)pT->CompareItem((LPCOMPAREITEMSTRUCT)lParam);

}

LRESULT OnDeleteItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)

{

T* pT = static_cast<T*>(this);

pT->SetMsgHandled(TRUE);

pT->DeleteItem((LPDELETEITEMSTRUCT)lParam);

bHandled = pT->IsMsgHandled();

return (LRESULT)TRUE;

}

// Overrideables

void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/)

{

// must be implemented

ATLASSERT(FALSE);

}

void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)

{

if(lpMeasureItemStruct->CtlType != ODT_MENU)

{

// return default height for a system font

T* pT = static_cast<T*>(this);

HWND hWnd = pT->GetDlgItem(lpMeasureItemStruct->CtlID);

CClientDC dc(hWnd);

TEXTMETRIC tm = { 0 };

dc.GetTextMetrics(&tm);

lpMeasureItemStruct->itemHeight = tm.tmHeight;

}

else

lpMeasureItemStruct->itemHeight = ::GetSystemMetrics(SM_CYMENU);

}

int CompareItem(LPCOMPAREITEMSTRUCT /*lpCompareItemStruct*/)

{

// all items are equal

return 0;

}

void DeleteItem(LPDELETEITEMSTRUCT /*lpDeleteItemStruct*/)

{

// default - nothing

}

};

上面的代码标红色的部分和wtl_for_mfc_programmers那个简短的说明的用法是一样的,以DrawItem作为说明吧,继承类CFontButtonImpl如果实现了DrawItem那么调用DrawItem就是继承类CFontButtonImpl自己的DrawItem,如果没有实现的话调用的就是COwnerDrawDrawItem,这里并没有用到虚函数,但是实现了虚拟函数一样的功能。

posted on 2010-03-31 09:18 漂漂 阅读(575) 评论(1)  编辑 收藏 引用 所属分类: WTL

FeedBack:
# re: wtl分析 2012-01-10 19:04 22
是用了隐藏机制.  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理