Chinaunix首页 | 论坛 | 博客
  • 博客访问: 16498039
  • 博文数量: 5645
  • 博客积分: 9880
  • 博客等级: 中将
  • 技术积分: 68081
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-28 13:35
文章分类

全部博文(5645)

文章存档

2008年(5645)

我的朋友

分类:

2008-04-28 20:58:13

下载本文示例代码
  导读   WTL 具有两面性,确实是这样的。它没有MFC的界面(GUI)类库那样功能强大,但是能够生成很小的可执行文件。如果你象我一样使用MFC进行界面编程,你会觉得MFC提供的界面控件封装使用起来非常舒服,更不用说MFC内置的消息处理机制。当然,如果你也象我一样不希望自己的程序仅仅因为使用了MFC的框架就增加几百K的大小的话,WTL就是你的选择。当然,我们还要克服一些障碍:  1) ATL样式的模板类初看起来有点怪异   2) 没有类向导的支持,所以要手工处理所有的消息映射。   3) MSDN没有正式的文档支持,你需要到处去收集有关的文档,甚至是查看WTL的源代码。   4) 买不到参考书籍   5) 没有微软的官方支持   6) ATL/WTL的窗口与MFC的窗口有很大的不同,你所了解的有关MFC的知识并不全部适用与WTL。     从另一方面讲,WTL也有它自身的优势:  1) 不需要学习或掌握复杂的文档/视图框架。   2) 具有MFC的基本的界面特色,比如DDX/DDV和命令状态的自动更新功能(译者加:比如菜单的Check标记和Enable标记)。   3) 增强了一些MFC的特性(比如更加易用的分隔窗口)。   4) 可生成比静态链接的MFC程序更小的可执行文件(译者加:WTL的所有源代码都是静态链接到你的程序中的)。   5) 你可以修正自己使用的WTL中的错误(BUG)而不会影响其他的应用程序(相比之下,如果你修正了有BUG的MFC/CRT动态库就可能会引起其它应用程序的崩溃。   如果你仍然需要使用MFC,MFC的窗口和ATL/WTL的窗口可以“和平共处”。(例如我工作中的一个原型就使用了了MFC的CFrameWnd,并在其内包含了WTL的CSplitterWindow,在CSplitterWindow中又使用了MFC的CDialogs -- 我并不是为了炫耀什么,只是修改了MFC的代码使之能够使用WTL的分割窗口,它比MFC的分割窗口好的多)。   在这一系列文章中,我将首先介绍ATL的窗口类,毕竟WTL是构建与ATL之上的一系列附加类,所以需要很好的了解ATL的窗口类。介绍完ATL之后我将介绍WTL的特性以并展示它是如何使界面编程变得轻而易举。  简介  WTL是个很酷的工具,在理解这一点之前需要首先介绍ATL。WTL是构建与ATL之上的一系列附加类,如果你是个严格使用MFC的程序员那么你可能没有机会接触到ATL的界面类,所以请容忍我在开始WTL之前先罗索一些别的东西,绕道来介绍一下ATL是很有必要地。  在本文的第一部分,我将给出一点ATL的背景知识,包括一些编写ATL代码必须知道的基本知识,快速的解释一些令人不知所措的ATL模板类和基本的ATL窗口类。  ATL 背景知识 ATL 和 WTL 的发展历史   “活动模板库”(Active Template Library)是一个很古怪的名字,不是吗?那些年纪大的人可能还记得它最初被称为“网络组件模板库”,这可能是它更准确的称呼,因为ATL的目的就是使编写组件对象和ActiveX控件更容易一些(ATL是在微软开发新产品ActiveX-某某的过程中开发的,那些ActiveX-某某现在被称为某某.NET)。由于ATL是为了便于编写组件对象而存在的,所以只提供了简单的界面类,相当于MFC的窗口类(CWnd)和对话框类(CDialog)。幸运的是这些类非常的灵活,能够在其基础上构建象WTL这样的附加类。  WTL现在已经是第二次修正了,最初的版本是3.1,现在的版本是7(WTL的版本号之所以这样选择是为了与ATL的版本匹配,所以不存在1和2这样的版本号)。WTL 3.1可以与VC 6和VC 7一起使用,但是在VC 7下需要定义几个预处理标号。WTL 7向下兼容WTL 3.1,并且不作任何修改就可以与VC 7一起使用,现在看来没有任何理由还使用3.1来进行新的开发工作。  ATL-style 模板   即使你能够毫不费力地阅读C 的模板类代码,仍然有两件事可能会使你有些头晕,以下面这个类的定义为例: class CMyWnd : public CWindowImpl<CMyWnd>{ ...};  这样作是合法的,因为C 的语法解释说即使CMyWnd类只是被部分定义,类名CMyWnd已经被列入递归继承列表,是可以使用的。将类名作为模板类的参数是因为ATL要做另一件诡秘的事情,那就是编译期间的虚函数调用机制。  如果你想要了解它是如何工作地,请看下面的例子: 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指派为D1或D2类型的指针,因为模板代码是在编译其间生成的,所以只要编译器生成正确的继承列表,这样指派就是安全的。(如果你写成: class D3 : public B1<D2>就会有麻烦) 之所以安全是因为this对象只可能是指向D1或D2(在某些情况下)类型的对象,不会是其他的东西。注意这很像C 的多态性(polymorphism),只是SayHi()方法不是虚函数。  要解释这是如何工作的,首先看对每个SayHi()函数的调用,在第一个函数调用,对象B1被指派为D1,所以代码被解释成: void B1<D1>::SayHi(){ D1* pT = static_cast<D1*>(this); pT->PrintClassName();}  由于D1没有重载PrintClassName(),所以查看基类B1,B1有PrintClassName(),所以B1的PrintClassName()被调用。  现在看第二个函数调用SayHi(),这一次对象被指派为D2类型,SayHi()被解释成: void B1<D2>::SayHi(){D2* pT = static_cast<D2*>(this);pT->PrintClassName();}  这一次,D2含有PrintClassName()方法,所以D2的PrintClassName()方法被调用。  这种技术的有利之处在于:  1) 不需要使用指向对象的指针。   2) 节省内存,因为不需要虚函数表。   3) 因为没有虚函数表所以不会发生在运行时调用空指针指向的虚函数。   4) 所有的函数调用在编译时确定(译者加:区别于C 的虚函数机制使用的动态编连),有利于编译程序对代码的优化。   节省虚函数表在这个例子中看起来无足轻重(每个虚函数只有4个字节),但是设想一下如果有15个基类,每个类含有20个方法,加起来就相当可观了。共4页。 1 2 3 4 :   导读   WTL 具有两面性,确实是这样的。它没有MFC的界面(GUI)类库那样功能强大,但是能够生成很小的可执行文件。如果你象我一样使用MFC进行界面编程,你会觉得MFC提供的界面控件封装使用起来非常舒服,更不用说MFC内置的消息处理机制。当然,如果你也象我一样不希望自己的程序仅仅因为使用了MFC的框架就增加几百K的大小的话,WTL就是你的选择。当然,我们还要克服一些障碍:  1) ATL样式的模板类初看起来有点怪异   2) 没有类向导的支持,所以要手工处理所有的消息映射。   3) MSDN没有正式的文档支持,你需要到处去收集有关的文档,甚至是查看WTL的源代码。   4) 买不到参考书籍   5) 没有微软的官方支持   6) ATL/WTL的窗口与MFC的窗口有很大的不同,你所了解的有关MFC的知识并不全部适用与WTL。     从另一方面讲,WTL也有它自身的优势:  1) 不需要学习或掌握复杂的文档/视图框架。   2) 具有MFC的基本的界面特色,比如DDX/DDV和命令状态的自动更新功能(译者加:比如菜单的Check标记和Enable标记)。   3) 增强了一些MFC的特性(比如更加易用的分隔窗口)。   4) 可生成比静态链接的MFC程序更小的可执行文件(译者加:WTL的所有源代码都是静态链接到你的程序中的)。   5) 你可以修正自己使用的WTL中的错误(BUG)而不会影响其他的应用程序(相比之下,如果你修正了有BUG的MFC/CRT动态库就可能会引起其它应用程序的崩溃。   如果你仍然需要使用MFC,MFC的窗口和ATL/WTL的窗口可以“和平共处”。(例如我工作中的一个原型就使用了了MFC的CFrameWnd,并在其内包含了WTL的CSplitterWindow,在CSplitterWindow中又使用了MFC的CDialogs -- 我并不是为了炫耀什么,只是修改了MFC的代码使之能够使用WTL的分割窗口,它比MFC的分割窗口好的多)。   在这一系列文章中,我将首先介绍ATL的窗口类,毕竟WTL是构建与ATL之上的一系列附加类,所以需要很好的了解ATL的窗口类。介绍完ATL之后我将介绍WTL的特性以并展示它是如何使界面编程变得轻而易举。  简介  WTL是个很酷的工具,在理解这一点之前需要首先介绍ATL。WTL是构建与ATL之上的一系列附加类,如果你是个严格使用MFC的程序员那么你可能没有机会接触到ATL的界面类,所以请容忍我在开始WTL之前先罗索一些别的东西,绕道来介绍一下ATL是很有必要地。  在本文的第一部分,我将给出一点ATL的背景知识,包括一些编写ATL代码必须知道的基本知识,快速的解释一些令人不知所措的ATL模板类和基本的ATL窗口类。  ATL 背景知识 ATL 和 WTL 的发展历史   “活动模板库”(Active Template Library)是一个很古怪的名字,不是吗?那些年纪大的人可能还记得它最初被称为“网络组件模板库”,这可能是它更准确的称呼,因为ATL的目的就是使编写组件对象和ActiveX控件更容易一些(ATL是在微软开发新产品ActiveX-某某的过程中开发的,那些ActiveX-某某现在被称为某某.NET)。由于ATL是为了便于编写组件对象而存在的,所以只提供了简单的界面类,相当于MFC的窗口类(CWnd)和对话框类(CDialog)。幸运的是这些类非常的灵活,能够在其基础上构建象WTL这样的附加类。  WTL现在已经是第二次修正了,最初的版本是3.1,现在的版本是7(WTL的版本号之所以这样选择是为了与ATL的版本匹配,所以不存在1和2这样的版本号)。WTL 3.1可以与VC 6和VC 7一起使用,但是在VC 7下需要定义几个预处理标号。WTL 7向下兼容WTL 3.1,并且不作任何修改就可以与VC 7一起使用,现在看来没有任何理由还使用3.1来进行新的开发工作。  ATL-style 模板   即使你能够毫不费力地阅读C 的模板类代码,仍然有两件事可能会使你有些头晕,以下面这个类的定义为例: class CMyWnd : public CWindowImpl<CMyWnd>{ ...};  这样作是合法的,因为C 的语法解释说即使CMyWnd类只是被部分定义,类名CMyWnd已经被列入递归继承列表,是可以使用的。将类名作为模板类的参数是因为ATL要做另一件诡秘的事情,那就是编译期间的虚函数调用机制。  如果你想要了解它是如何工作地,请看下面的例子: 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指派为D1或D2类型的指针,因为模板代码是在编译其间生成的,所以只要编译器生成正确的继承列表,这样指派就是安全的。(如果你写成: class D3 : public B1<D2>就会有麻烦) 之所以安全是因为this对象只可能是指向D1或D2(在某些情况下)类型的对象,不会是其他的东西。注意这很像C 的多态性(polymorphism),只是SayHi()方法不是虚函数。  要解释这是如何工作的,首先看对每个SayHi()函数的调用,在第一个函数调用,对象B1被指派为D1,所以代码被解释成: void B1<D1>::SayHi(){ D1* pT = static_cast<D1*>(this); pT->PrintClassName();}  由于D1没有重载PrintClassName(),所以查看基类B1,B1有PrintClassName(),所以B1的PrintClassName()被调用。  现在看第二个函数调用SayHi(),这一次对象被指派为D2类型,SayHi()被解释成: void B1<D2>::SayHi(){D2* pT = static_cast<D2*>(this);pT->PrintClassName();}  这一次,D2含有PrintClassName()方法,所以D2的PrintClassName()方法被调用。  这种技术的有利之处在于:  1) 不需要使用指向对象的指针。   2) 节省内存,因为不需要虚函数表。   3) 因为没有虚函数表所以不会发生在运行时调用空指针指向的虚函数。   4) 所有的函数调用在编译时确定(译者加:区别于C 的虚函数机制使用的动态编连),有利于编译程序对代码的优化。   节省虚函数表在这个例子中看起来无足轻重(每个虚函数只有4个字节),但是设想一下如果有15个基类,每个类含有20个方法,加起来就相当可观了。共4页。 1 2 3 4 : 下载本文示例代码


MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类MFC程序员的WTL开发指南之ATL界面类
阅读(111) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~