现在的位置: 首页 > 综合 > 正文

(转)WTL入门(1)– ATL背景知识

2013年02月14日 ⁄ 综合 ⁄ 共 2723字 ⁄ 字号 评论关闭

本文适用于对MFC比较了解的中级开发人员。

源代码下载:http://download.csdn.net/source/3522785

ATL Background

ATL-style templates

 

[html] view
plain
copy

  1. class CMyWnd : public CWindowImpl<CMyWnd>  
  2.   
  3. {  
  4.   
  5. ...  
  6.   
  7. };  

这是合法的。因为C++规范上说在class CMyWnd之后CmyWnd立即被定义并且可以用于继承列表。ATL中用这种方法可以实现编译时虚函数调用。

看下面例子:

[html] view
plain
copy

  1. template <class T>  
  2.   
  3. class B1  
  4.   
  5. {  
  6.   
  7. public:   
  8.   
  9.     void SayHi()   
  10.   
  11. {  
  12.   
  13.     // 此处是关键技巧。  
  14.   
  15.         T* pT = static_cast<T*>(this);     
  16.   
  17.    
  18.   
  19.         pT->PrintClassName();  
  20.   
  21.     }  
  22.   
  23.     void PrintClassName() { cout << "This is B1"; }  
  24.   
  25. };  
  26.   
  27.    
  28.   
  29. class D1 : public B1<D1>  
  30.   
  31. {  
  32.   
  33.     // No overridden functions at all  
  34.   
  35. };  
  36.   
  37.    
  38.   
  39. class D2 : public B1<D2>  
  40.   
  41. {  
  42.   
  43.     void PrintClassName() { cout << "This is D2"; }  
  44.   
  45. };  
  46.   
  47.    
  48.   
  49. int main()  
  50.   
  51. {  
  52.   
  53. D1 d1;  
  54.   
  55. D2 d2;  
  56.   
  57.    
  58.   
  59. d1.SayHi();    // prints "This is B1"  
  60.   
  61. d2.SayHi();    // prints "This is D2"  
  62.   
  63.    
  64.   
  65. return 0;  
  66.   
  67. }  

static_cast<T*>(this)是关键技术。它将B1*this根据不同的调用转化为D1*D2*。由于模板代码是在编译时生成的,只要继承列表编写正确,这种类型转换就是安全的。(避免写出class D3
public B1<D2>这种形式的代码。编译器是检测不出的。)

第一次调用SayHi()时,代码实际是这样的:

  1. void B1<D1>::SayHi()  
  2.   
  3. {  
  4.   
  5. D1* pT = static_cast<D1*>(this);  
  6.   
  7.     pT->PrintClassName();  
  8.   
  9. }  

D1没有重写PrintClassName(),因此去搜索D1的基类。B1有PrintClassName(),因此实际调用B1::PrintClassName()。

  1. void B1<D2>::SayHi()  
  2.   
  3. {  
  4.   
  5. D2* pT = static_cast<D2*>(this);  
  6.   
  7.     pT->PrintClassName();  
  8.   
  9. }  

D2重写了函数PrintClassName(),直接调用此函数。

这种技术的优点:

1) 不需要使用对象的指针

2) 节省内存,因为不需要使用虚函数表

3) 不会因为未初始化的虚函数表导致使用NULL指针

4) 所有函数的调用在编译时确定,因此它们是可以优化的。

ATL Windowing Classes

ATL严格遵守接口-实现分离的原则。

ATL拥有一个定义Window的接口类:CWindow。它仅仅封装了HWND,并且封装了几乎所有的Use32 APIs中以HWND为第一个参数的接口。例如SetWindowText() 和DestroyWindow()CWindow提供一个共有的成员m_hWnd,可以直接处理HWND,也提供了一个operator
HWND()
,可以直接是CWindow作为需要HWND对象的函数参数。

CWindow不同于MFC中的 CWndCWindow易于创建的,它不提供像MFC中的HWNDCWnd的对象关系。当CWindow对象超出作用域时,它被销毁,但是它关联的实际窗口不会被销毁。因此不需要detach你创建的临时的CWindow对象。

ATL还提供了一个Window的实现类CWindowImpl。它包含了下列处理:窗口注册、窗口子类、消息映射、以及一个基本的WindowProc().不想MFC中的CWnd,所有的东西都在这个类中。

关于对话框的实现,ATL提供了两个独立的实现类CDialogImpl 和 CAxDialogImpl。前者用于普通对话框 ,后者用于ActiveX控件。

Defining a Window Implementation

定义一个非对话框的窗口类,要从CWindowImpl派生。新类中必须包含三件事情:

1) 窗口类定义

2) 消息映射

3) 一个默认的窗体特征,叫做window traits

1、 窗口类定义用宏DECLARE_WND_CLASS 或 DECLARE_WND_CLASS_EX.二者均定义了一个ATL结构CWndClassInfo,它封装了WNDCLASSEX结构。前者仅仅定义新的窗口类的名字,其他参数用默认值;后者还可以定义窗口风格和背景色。窗口类的名字可以是NULL,此时ATL会自动生成一个。

  1. class CMyWindow : public CWindowImpl<CMyWindow>  
  2. {  
  3. public:  
  4.     DECLARE_WND_CLASS(_T("My Window Class"))  
  5. };  

2、 消息映射,与MFC类似。ATL 消息映射将之扩展为一个Switch语句,选择正确的句柄执行对应的函数。

  1. class CMyWindow : public CWindowImpl<CMyWindow>  
  2. {  
  3. public:  
  4.     DECLARE_WND_CLASS(_T("My Window Class"))  
  5.     BEGIN_MSG_MAP(CMyWindow)  
  6.     END_MSG_MAP()  
  7. };  

3、 定义window traits. 它是一个窗体风格和用于创建窗体的风格的组合。它们将作为模板参数来封装窗体特征类,这样调用者不必困扰于当创建窗口时如何获取正确的窗体风格。

抱歉!评论已关闭.