面向对象大家都清楚。现在OO的主流是基于类的面向对象。子类化故名思义就是定义一个子类,也就是要订制一个类变成一个新类的意思。
不过Windows的子类化不是如此。
首先,我们知道,除了基于类的面向对象以外,还有基于原型(或者叫基于对象)的面向对象。Windows是一个基于对象的OS,它的对象系统更倾向于基于原型的OO。Windows开发的一个核心概念是Window,也就是常说的窗口,所有的一切别的东西都是围绕着这个展开的。一个Windows应用程序,其基本的逻辑框架就是:创建一个窗口,接收系统发给该窗口的消息进行处理。收到结束消息的时候,关掉窗口,程序完蛋。
Window(注意,不是 Windows)相关的一个概念叫作WindowClass,窗口类。窗口类是个啥东西?呵呵,窗口类也是一个对象,具体的可以认为是Window的原型。创建一个WindowClass对象,可以基于该窗口类对象创造多个Window,这些Window在某一方面都是一致的。举个例子,有一个窗口类叫做Button,以这个Button为原型创建的所有Window在某一方面都是一样的。哪一方面呢?某些样式,消息处理例程,菜单等等等等。这些都是 WindowClass里面规定的东西。
那位说了,哦,知道了,基于窗口类原型创建出来Window,这个过程就是子类化。呵呵,广义上说,这是对的,就是特化了一些东西。但Windows子类化有个特殊的含义,它是特指某种行为的。
WindowClass作为一个对象,拥有对象的一切特征,其中包括修改其状态(或属性)的能力。WindowClass有一个属性叫做消息处理器,其实是一个函数指针,指向一个处理发送给它的实例(也就是那些以它为原型的Window)的消息的函数。
所谓的子类化,就是替换这个函数指针成员,用一个新的消息处理函数替代原来的消息处理函数。你或许会觉得奇怪,为什么这个动作叫做子类化?仔细想想其实也不奇怪,不就是修改了WindowClass的行为了么,这个就是子类化了:)
现在我们可以深入的分析一下这个消息处理函数的特征了。
它的职责是处理OS分发给它的各种消息,所以他的典型结构如下:
Handle
{
switch (msg.name)
{
case msgA:
processMsgA(msg.params);
break;
...
default:
windowsSystemDefaultProcess(msg);
break;
}
}
对于所有感兴趣的消息,都可以加一个分支处理。于是很多人开始不满了,觉得有坏气味,要重构。重构的方式是把消息和处理做成一个map,这样消息处理就变成:
Handle
{
if (map[msg.name])
{
map[msg.name](msg.params);
return;
}
return windowsSystemDefaultProcess(msg);
}
剩下的问题就是怎么充填这个消息到处理的映射了。
这个充填的方式,MFC采用了宏(其实大多数C++框架都采用了宏),但是宏并不让人满意。采用宏也是历史原因导致的,那时候C++并没有STL这么好用的容器库。有了map,我们再采用宏的方式生成一个映射数组的方式已经过时了。
有了这一步的重构,我们就可以一个函数针对一个消息进行处理了,CWnd就帮我们干了这样一件事。我们要是相对某个特定的消息进行特定的处理,可以给它一个特定的处理函数,放在map中替换原来的处理函数即可。还有另一种方式就是override从CWnd继承下来的消息处理的虚函数,使用C++的动态分派能力进行特定化。
不过Windows的子类化不是如此。
首先,我们知道,除了基于类的面向对象以外,还有基于原型(或者叫基于对象)的面向对象。Windows是一个基于对象的OS,它的对象系统更倾向于基于原型的OO。Windows开发的一个核心概念是Window,也就是常说的窗口,所有的一切别的东西都是围绕着这个展开的。一个Windows应用程序,其基本的逻辑框架就是:创建一个窗口,接收系统发给该窗口的消息进行处理。收到结束消息的时候,关掉窗口,程序完蛋。
Window(注意,不是 Windows)相关的一个概念叫作WindowClass,窗口类。窗口类是个啥东西?呵呵,窗口类也是一个对象,具体的可以认为是Window的原型。创建一个WindowClass对象,可以基于该窗口类对象创造多个Window,这些Window在某一方面都是一致的。举个例子,有一个窗口类叫做Button,以这个Button为原型创建的所有Window在某一方面都是一样的。哪一方面呢?某些样式,消息处理例程,菜单等等等等。这些都是 WindowClass里面规定的东西。
那位说了,哦,知道了,基于窗口类原型创建出来Window,这个过程就是子类化。呵呵,广义上说,这是对的,就是特化了一些东西。但Windows子类化有个特殊的含义,它是特指某种行为的。
WindowClass作为一个对象,拥有对象的一切特征,其中包括修改其状态(或属性)的能力。WindowClass有一个属性叫做消息处理器,其实是一个函数指针,指向一个处理发送给它的实例(也就是那些以它为原型的Window)的消息的函数。
所谓的子类化,就是替换这个函数指针成员,用一个新的消息处理函数替代原来的消息处理函数。你或许会觉得奇怪,为什么这个动作叫做子类化?仔细想想其实也不奇怪,不就是修改了WindowClass的行为了么,这个就是子类化了:)
现在我们可以深入的分析一下这个消息处理函数的特征了。
它的职责是处理OS分发给它的各种消息,所以他的典型结构如下:
Handle
{
switch (msg.name)
{
case msgA:
processMsgA(msg.params);
break;
...
default:
windowsSystemDefaultProcess(msg);
break;
}
}
对于所有感兴趣的消息,都可以加一个分支处理。于是很多人开始不满了,觉得有坏气味,要重构。重构的方式是把消息和处理做成一个map,这样消息处理就变成:
Handle
{
if (map[msg.name])
{
map[msg.name](msg.params);
return;
}
return windowsSystemDefaultProcess(msg);
}
剩下的问题就是怎么充填这个消息到处理的映射了。
这个充填的方式,MFC采用了宏(其实大多数C++框架都采用了宏),但是宏并不让人满意。采用宏也是历史原因导致的,那时候C++并没有STL这么好用的容器库。有了map,我们再采用宏的方式生成一个映射数组的方式已经过时了。
有了这一步的重构,我们就可以一个函数针对一个消息进行处理了,CWnd就帮我们干了这样一件事。我们要是相对某个特定的消息进行特定的处理,可以给它一个特定的处理函数,放在map中替换原来的处理函数即可。还有另一种方式就是override从CWnd继承下来的消息处理的虚函数,使用C++的动态分派能力进行特定化。