winlinglin

2009年5月31日

结束 == 新的开始?

2009年5月31号下午2点半,我离开了我的学校,离开我的哥们,离开了生活了充实的4年的地方...
明天,我就正式成为程序员了。我是多么地幸运,能将兴趣当成工作,我发誓,我尽我的能力好好干。
我没忘记我的理想,没忘记我的抱负。
明天,我掀开我人生的新一页...

posted @ 2009-05-31 23:00 wil 阅读(231) | 评论 (0)编辑 收藏

Windows API(2) MessageBox,GetDesktopWindow

请看一个例子:
 1#include <windows.h>
 2#include <tchar.h>
 3
 4
 5int APIENTRY _tWinMain(          
 6                   HINSTANCE hInstance,
 7                   HINSTANCE hPrevInstance,
 8                   LPSTR lpCmdLine,
 9                   int nCmdShow
10                   )
11{
12    UNREFERENCED_PARAMETER( hInstance );
13    UNREFERENCED_PARAMETER( hPrevInstance );
14    UNREFERENCED_PARAMETER( lpCmdLine );
15    UNREFERENCED_PARAMETER( nCmdShow );
16
17    HWND hwnd = ::GetDesktopWindow();
18
19    ::MessageBox( hwnd, _T( "First Windows" ), _T( "Example" ), MB_OK );
20
21    return 0;
22}

这个例子是大山哥哥在他的BLOG中Windows API精讲一日一练的例子。
奇怪吧,这次的WinMain函数又不一样了。Windows编程难学,就是因为它那么多宏,它伟大之处也是因为它的宏,可以屏蔽硬件层、字符集对程序的影响。
一般的WinMain函数是这样写的: int WINAPI WinMain(...),那现在的这种又是怎样出来的呢?
APIENTRY在程序就是一个宏:#define APIENTRY WINAPI,那么我们就可以不管那个前缀了。
你肯定想不到的是,_tWinMain也是一个宏。它是定义在<tchar.h>中的。你可以试试将#include <tchar.h>去掉,你的程序肯定找不到入口点。
同样的,_T也是一个宏。
这两个宏其实是为了适应UNICODE和以前单字符的应用程序。
由于Windows这两个API的定义是不一样的,如下:
UNICODE的定义:
 #define _tWinMain   wWinMain
单字符的定义:
 #define _tWinMain   WinMain
只要经过这样的宏定义后,就可以适应不同字符宽度的函数接口了。

好,看函数里面。
函数里面第一句又是一个宏,晕...怎么老是它...不过要在Windows编程,必须爱上它。
这个宏与Windows API无关,UNREFERENCED_PARAMETER 展开传递的参数或表达式,其目的是避免编译器关于未引用参数的警告。当然你也可以在编译器属性那里
修改编译的警告级别。
例子中的4句宏,就是为了关掉编译器对WinMain函数4个参数未引用的报错。避免了几个警告的产生,呵呵。。。

GetDesktopWindow这个该不是宏了吧,哈哈。。。
MSDN上说:The GetDesktopWindow function returns a handle to the desktop window. The desktop window covers the entire screen.
The desktop window is the area on top of which other windows are painted.
这个函数返回了桌面的Handle,桌面大家都知道吧?

MessageBox想不到也是一个宏,它的定义是:
#ifdef UNICODE
#define MessageBox  MessageBoxW
#else
#define MessageBox  MessageBoxA
#endif // !UNICODE
很明显是为了兼容字符集参数而定义的。难道Windows API函数的参数有字符串的,都是一个宏吗?
MessageBox可能见得很多,最怕的大概就是“警告:内存出错,程序退出”了。
这是MessageBox函数的声明
int MessageBox(        
    HWND hWnd,
    LPCTSTR lpText,
    LPCTSTR lpCaption,
    UINT uType
);
参数一hWnd是一个HWND,它是这个MessageBox属于的窗口的HWND,如果它为NULL,则MessageBox不属于任何一个窗口。
参数二lpText是这个MessageBox显示的消息内容,为了迎合Microsoft的好意,我们都应该用宏来传入参数,来兼容字符串。
参数三lpCaption是这个MessageBox的标题内容。
参数四uType则是这个MessageBox的类型,它可以改变整个MessageBox的行为和样式哦。它是一组符号标识(flags),如:MB_OK。可以用"|"操作符来进行组合
返回值呢?当然是你在MessageBox上的操作啦。返回值一般用宏来表示,如:IDABORT,IDOK。

posted @ 2009-05-31 10:57 wil 阅读(1233) | 评论 (0)编辑 收藏

Windows API(1) WinMain函数

谁是第一个Windows API?无可置疑,当然是WinMain Function啦。
MSDN上说:The WinMain function is the conventional name for the user-provided entry point for a Microsoft Windows-based application.
即:WinMain函数是Microsoft的一个传统函数命名,它是提供给用户的Windows应用程序的入口点。
它的函数声明如下:
int WINAPI WinMain(         
 HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
);
写惯C++ console程序的人可能会很奇怪,main函数一般不是这样写吗( int main() )?怎么会在函数声明中间加了个词(WINAPI)呢?
其实WINAPI是一个宏,MSDN上说:Calling convention for system functions. This type is declared in WinDef.h as follows: #define WINAPI __stdcall
那么WINAPI就是指_stdcall了。
在网上查了一下,_stdcall还有其他同类,_cdecl _pascal _fastcall...怎么那么多的?
_stdcall _cdecl _pascal _fastcall这些关键字是什么意思,有什么区别呢?
在网上查了一下,总结一下答案:
(1)其实它们就是关于堆栈的一些说明,首先是函数参数压栈顺序,其次是压入堆栈的内容由谁来清除,调用者还是函数自己?
  这些开关用来告诉编译器产生什么样的汇编代码。
(2)VC有两种函数调用方式   一种是__stdcall,另一种是__cdecl  
 函数的调用方式有两种一种是PASCAL调用方式(_stdcall),另一种是C调用方式(_cdecl)  
 使用PASCAL调用方式,函数在返回到调用者之前将参数从栈中删除  
 使用C调用方式,参数的删除是调用者完成的  
 WinMain函数是由系统调用的,Windows系统规定由系统调用的函数都遵守PASCAL调用方式  
 但是VC中函数的缺省调用方式是__cdecl,也就是C调用方式  
 所以在WinMain前显示的声明。  
 在Windows编程中将遇到很多声明修饰符,如CALLBACK,WINAPI,PASCAL这些在Intel CPU的计算机上都是__stdcall
(3)__cdecl是C/C++和MFC程序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdecl约定时,
 函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。
 因此,实现可变参数的函数只能使用该调用约定。
 由于每一个使用__cdecl约定的函数都要包含清理堆栈的代码,所以产生的可执行文件大小会比较大。
 __cdecl可以写成_cdecl。
 __stdcall调用约定用于调用Win32 API函数。采用__stdcal约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,
 函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。
 __stdcall可以写成_stdcall。
 __fastcall约定用于对性能要求非常高的场合。__fastcall约定将函数的从左边开始的两个大小不大于
 4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送
 参数的堆栈。
 __fastcall可以写成_fastcall。
(4)thiscall仅仅应用于“C++”成员函数。this指针存放于CX/ECX寄存器中,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
(5)naked call。当采用其他的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,
 退出函数时则产生代码恢复这些寄存器的内容。

·特别说明
1. 在默认情况下,采用__cdecl方式,因此可以省略.
2. WINAPI一般用于修饰动态链接库中导出函数
3. CALLBACK仅用于修饰回调函数
4. 你可能已经发现,VC下和BCB下对WINAPI的定义不同,那么你至少理解了
   为什么不能直接从BCB下调用VC的dll的一个原因了。
  
不查不知道,一查吓一跳,怎么那么多规则的?整理了一下思路,其实并不复杂。
VC默认的是_cdecl方式,Win32 API函数是用_stdcall方式的,他们都是将函数参数从右到左入栈的。
_cdecl方式的每个函数都有清理堆栈的代码,可以实现可变参数列表,但可执行文件大小比较大。_stdcall方式是调用者清理堆栈的。
_fastcall的特点是它将参数左边的两个参数放在寄存器上,比较快。其余参数还是在堆栈中,堆栈还是由函数自己清除。
其它就不太清楚了。

好,该看看函数的参数了,hInstance是当前应用程序实例的Handle.
第二个参数hPrevInstance应用程序上一个实例的Handle。MSDN说:如果你要知道应用程序是否有另一个实例,建议使用Mutex(互斥体)来实现。此时,我想到了
单例模式,用Mutex来实现只运行一个实例。
第三个参数lpCmdLine是一个字符串,是命令行参数。
第四个参数nCmdShow是一个int,指明Window应该怎么现实,Windows定义了一系列宏,来帮助记忆,以SW开头,如:SW_SHOW

最后是返回值,它是一个int。
MSDN说:If the function succeeds, terminating when it receives a WM_QUIT message, it should return the exit value contained in that message's wParam parameter. If the function terminates before entering the message loop, it should return zero.
如果它成功的话,它会一直运行,知道收到WM_QUIT消息,它应该返回消息的wParam参数的退出值。如果函数在进入消息循环前退出,它应该返回0。
 

posted @ 2009-05-31 09:15 wil 阅读(1595) | 评论 (2)编辑 收藏

2009年3月8日

遗传算法(转)

现代生物遗传学中描述的生物进化理论:
遗传物质的主要载体是染色体(chromsome),染色体主要由DNA和蛋白质组成。其中DNA为最主要的遗传物质。
基因(gene)是有遗传效应的片断,它存储着遗传信息,可以准确地复制,也能发生突变,并可通过控制蛋白质的合成而控制生物的状态.生物自身通过对基因的复制(reproduction)和交叉(crossover,即基因分离,基因组合和基因连锁互换)的操作时其性状的遗传得到选择和控制。生物的遗传特性,使生物界的物种能保持相对的稳定;生物的变异特性,使生物个体产生新的性状,以至于形成了新的物种(量变积累为质变),推动了生物的进化和发展。

遗传学算法和遗传学中的基础术语比较

染色体(chromosome)    数据,数组,序列
基因(gene) 单个元素,位
等位基因(allele) 数据值,属性,值
基因座(locus)  位置,iterator位置
表现型(phenotype)  参数集,解码结构,候选解
遗传隐匿(epistasis)  非线性

染色体又可以叫做基因型个体(individuals),一定数量的个体组成了群体(population),群体中个体的数量叫做群体大小。各个个体对环境的适应程度叫做适应度(fitness)

遗传算法的准备工作:
1)数据转换操作,包括表现型到基因型的转换和基因型到表现型的转换。前者是把求解空间中的参数转化成遗传空间中的染色体或者个体(encoding),后者是它的逆操作(decoding)
2)确定适应度计算函数,可以将个体值经过该函数转换为该个体的适应度,该适应度的高低要能充分反映该个体对于解得优秀程度。非常重要的过程!

遗传算法的基本步骤
遗传算法是具有"生成+检测"(generate-and-test)的迭代过程的搜索算法。
基本过程为:
1)编码,创建初始集团
2)集团中个体适应度计算
3)评估适应度
4)根据适应度选择个体
5)被选择个体进行交叉繁殖,
6)在繁殖的过程中引入变异机制
7)繁殖出新的集团,回到第二步

一个简单的遗传算法的例子:求 [0,31]范围内的y=(x-10)^2的最小值
1)编码算法选择为"将x转化为2进制的串",串的长度为5位。(等位基因的值为0 or 1)
2)计算适应度的方法是:先将个体串进行解码,转化为int型的x值,然后使用y=(x-10)^2作为其适应度计算合适(由于是最小值,所以结果越小,适应度也越好)
3)正式开始,先设置群体大小为4,然后初始化群体 => (在[0,31]范围内随机选取4个整数就可以,编码)
4)计算适应度Fi(由于是最小值,可以选取一个大的基准线1000,Fi = 1000 - (x-10)^2)
5)计算每个个体的选择概率.选择概率要能够反映个体的优秀程度.这里用一个很简单的方法来确定选择概率
P=Fi / TOTAL(Fi).
6)选择.
根据所有个体的选择概率进行淘汰选择.这里使用的是一个赌轮的方式进行淘汰选择.先按照每个个体的选择概率创建一个赌轮,然后选取4次,每次先产生一个0-1的随机小数,然后判断该随机数落在那个段内就选取相对应的个体.这个过程中,选取概率P高的个体将可能被多次选择,而概率低的就可能被淘汰.

下面是一个简单的赌轮的例子
   13%               35%                    15%                 37%       
----------|----------------------------|------------|-*-------------------------|
   个体1              个体2                  个体3    ^0.67    个体4

随机数为0.67落在了个体4的端内.本次选择了个体4. 

被选中的个体将进入配对库(mating pool,配对集团)准备开始繁殖.
7)简单交叉
先对配对库中的个体进行随机配对.然后在配对的2个个体中设置交叉点,交换2个个体的信息后产生下一代.
比如( | 代表简单串的交叉位置)
 ( 0110|1, 1100|0 ) --交叉--> (01100,11001)
 ( 01|000, 11|011 ) --交叉--> (01011,11000)
2个父代的个体在交叉后繁殖出了下一代的同样数量的个体.
复杂的交叉在交叉的位置,交叉的方法,双亲的数量上都可以选择.其目的都在于尽可能的培育出更优秀的后

8)变异
变异操作时按照基因座来的.比如说没计算2万个基因座就发生一个变异(我们现在的每个个体有5个基因座.也就是说要进化1000代后才会在其中的某个基因座发生一次变异.)变异的结果是基因座上的等位基因发生了变化.我们这里的例子就是把0变成1或则1变成0.
至此,我们已经产生了一个新的(下一代)集团.然后回到第4步,周而复始,生生不息下去:)

伪代码实例(适合爱看代码的朋友~):

//Init population
foreach individual in population
{
     individual = Encode(Random(0,31));
}

while (App.IsRun)

     //计算个体适应度
     int TotalF = 0;
     foreach individual in population
     {
      individual.F = 1000 - (Decode(individual)-10)^2;
      TotalF += individual.F;
     }

     //------选择过程,计算个体选择概率-----------
     foreach individual in population
     {
          individual.P = individual.F / TotalF;
     }
     //选择
     for(int i=0;i<4;i++)
     {
          //SelectIndividual(float p)是根据随机数落在段落计算选取哪个个体的函数
          MatingPool[i] = population[SelectIndividual(Random(0,1))];
     }
     //-------简单交叉---------------------------
     //由于只有4个个体,配对2次
     for(int i=0;i<2;i++)
     {
          MatingPool.Parents[i].Mother = MatingPool.RandomPop();
          MatingPool.Parents[i].Father = MatingPool.RandomPop();
        }

     //交叉后创建新的集团
     population.Clean();
     foreach Parent in MatingPool.Parents
     {
          //注意在copy 双亲的染色体时在某个基因座上发生的变异未表现.
          child1 = Parent.Mother.DivHeader + Parent.Father.DivEnd;
          child2 = Parent.Father.DivHeader + Parent.Mother.DivEnd;
          population.push(child1);
          population.push(child2);
     }

小结:
遗传算法中最重要的过程就是选择和交叉。
选择要能够合理的反映"适者生存"的自然法则,而交叉必须将由利的基因尽量遗传给下一代(这个算法很关键!)
还有就是编码的过程要能够使编码后的染色体能充分反映个体的特征并且能够方便计算。

这篇文章是原来学习的一些回忆的整理,因为最近要实用了.不正确的地方还希望大家多多指出~

posted @ 2009-03-08 20:13 wil 阅读(338) | 评论 (0)编辑 收藏

2009年3月5日

螺旋数组

今天想练练手,所以写了个螺旋数组:
  1 #include <iostream>
  2 
  3 using namespace std;
  4 
  5 #define MAXSIZE 8
  6 
  7 void left( int& x, int& y )
  8 {
  9     --y;
 10 }
 11 
 12 void right( int& x, int& y )
 13 {
 14     ++y;
 15 }
 16 
 17 void up( int& x, int& y )
 18 {
 19     --x;
 20 }
 21 
 22 void down( int& x, int& y )
 23 {
 24     ++x;
 25 }
 26 
 27 int main()
 28 {
 29     int numbers[MAXSIZE][MAXSIZE];
 30     // 初始化数组,若数值为0,则代表还没有被赋值
 31     forint i = 0; i<MAXSIZE; ++i )
 32         forint j = 0; j<MAXSIZE; ++j )
 33             numbers[i][j] = 0;
 34 
 35     enum Direction{RIGHT,DOWN,LEFT,UP};    // 移动方向
 36     int x=0,y=0;
 37     int Next = -1;    // 下一个位置的值
 38     Direction direct = RIGHT;
 39     int count = MAXSIZE * MAXSIZE;    // 还没被赋值的数目
 40     int value = 1;    //将要被赋值的值
 41 
 42     while( count > 0 )
 43     {
 44         Next = numbers[x][y];
 45         if( Next == 0 && x<MAXSIZE && y<MAXSIZE )    // 无障碍,可以赋值
 46         {
 47             numbers[x][y] = value;
 48             // 赋值成功,count减一, value加一
 49             --count;
 50             ++value;
 51 
 52             // 设置Next
 53             if( direct == RIGHT )
 54                 right( x, y );
 55             else if( direct == DOWN )
 56                 down( x, y );
 57             else if( direct == LEFT )
 58                 left( x, y );
 59             else if( direct == UP )
 60                 up( x, y );
 61         }
 62         else        // 有障碍,要转弯
 63         {
 64             if( direct == RIGHT )    // 若原来方向是右的话,就转弯向下
 65             {
 66                 x = x + 1;
 67                 y = y - 1;
 68                 direct = DOWN;
 69             }
 70             else if( direct == DOWN )    //若原来方向是下的话,就转弯向左
 71             {
 72                 x = x - 1;
 73                 y = y - 1;
 74                 direct = LEFT;
 75             }
 76             else if( direct == LEFT )    //若原来方向是左的话,就转弯向上
 77             {                
 78                 x = x - 1;
 79                 y = y + 1;
 80                 direct = UP;
 81             }
 82             else if( direct == UP)    //若原来方向是上的话,就转弯向右
 83             {                
 84                 x = x + 1;
 85                 y = y + 1;
 86                 direct = RIGHT;
 87             }
 88         }
 89     }
 90 
 91     forint i = 0; i<MAXSIZE; ++i )
 92     {
 93         forint j = 0; j<MAXSIZE; ++j )
 94         {
 95             cout<<numbers[i][j]<<"   ";
 96         }
 97         cout<<endl;
 98     }
 99 
100     return 0;
101 }

posted @ 2009-03-05 18:33 wil 阅读(1115) | 评论 (2)编辑 收藏

2009年3月4日

#define 的用法

宏的单行定义
#define A(x) T_##x
#define B(x) #@x

#define C(x) #x
我们假设:x=1,则有:
A(1)------〉T_1
B(1)------〉'1'
C(1)------〉"1"

##代表“连接”
#@代表“转为字符”
#代表 “转为字符串”


define的多行定义
define可以替代多行的代码,例如MFC中的宏定义(非常的经典)

#define MACRO(arg1, arg2) do { \
/* declarations */ \
stmt1; \
stmt2; \
/* ... */ \
} while(0) /* (no trailing ; ) */
关键是要在每一个换行的时候加上一个"\" 

posted @ 2009-03-04 23:08 wil 阅读(543) | 评论 (0)编辑 收藏

2009年3月3日

MFC学习笔记(一)

Win32应用程序有条明确的主线:
 (1) 进入WinMain函数
 (2) 设计一个Window
 (3) 注册这个Window
 (4) 建立这个Window
 (5) 显示和更新这个Window
 (6) 进入消息循环
 
好,我就先找WinMain函数吧。
我在C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc的appmodul.cpp的23行中找到了以下代码:
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 _In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress: 4985)
{
 // call shared/exported WinMain
 return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

_tWinMain是一个宏,详细为: #define _tWinMain WinMain
所以这个确实是我们要找的WinMain函数

从代码中看出,WinMain将参数全部交给AfxWinMain,来处理。
好,我又找AfxWinMain这个函数。

我在winmain.cpp的19行找到了AfxWinMain函数。
代码:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 _In_ LPTSTR lpCmdLine, int nCmdShow)
{
 ASSERT(hPrevInstance == NULL);   // ASSERT在程序运行时它计算括号内的表达式,如果表达式为FALSE (0),
           // 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。
           // ASSERT只有在Debug版本中才有效,如果编译为Release版本则被忽略。
           // assert()的功能类似,它是ANSI C标准中规定的函数,它与ASSERT的一个重要区别是可以用在Release版本中。
 int nReturnCode = -1;
 // AfxGetThread和 AfxGetApp 都是全局函数
 CWinThread* pThread = AfxGetThread();  // 获得正在执行的线程,Must be called from within the desired thread.
 CWinApp* pApp = AfxGetApp();    // 获得A pointer to the single CWinApp object for the application

 // AFX internal initialization
            // This function is called by the MFC-supplied WinMain function, as part of the CWinApp initialization of a GUI-based
            // application, to initialize MFC.
 if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))  
  goto InitFailure;

 // App global initializations (rare)
 if (pApp != NULL && !pApp->InitApplication())  //InitApplication已经过时,用InitInstance代替,完成MFC内部管理方面的工作
  goto InitFailure;

 // Perform specific initializations
 if (!pThread->InitInstance())  // 初始化Instance,在每个 a copy of the program runs的时候,虚函数
 {
  if (pThread->m_pMainWnd != NULL)
  {
   TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
   pThread->m_pMainWnd->DestroyWindow();   // m_pMainWnd holds a pointer to the application's main window.返回一个CWnd.
               // cWnd Destroys the attached Windows window.
  }
  nReturnCode = pThread->ExitInstance();    // to exit this instance of the thread
  goto InitFailure;
 }
 nReturnCode = pThread->Run();  

InitFailure:
#ifdef _DEBUG
 // Check for missing AfxLockTempMap calls
 if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
 {
  TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
   AfxGetModuleThreadState()->m_nTempMapLock);
 }
 AfxLockTempMaps();
 AfxUnlockTempMaps(-1);
#endif

 AfxWinTerm();
 return nReturnCode;
}


找到了WinMain函数后,看了下MFC为我生成的类:
1. CTestApp  2. CTestView 3. CMainFrame  4. CTestDoc  5. CAboutDlg
查看CTestApp.cpp,发现了一个全局的CTestApp theApp,因为全局对象必须在main函数之前产生并初始化,所以应用程序调用的顺序应该是
CTestApp的构造函数 -> WinMain函数
又发现class CTestApp : public CWinApp,子类的构造函数在父类的构造函数调用之后调用,所以就搜索CWinApp吧。

在appcore.cpp的368行发现以下代码:
CWinApp::CWinApp(LPCTSTR lpszAppName)  // 此处的lpszAppName有个默认参数NULL
{
 if (lpszAppName != NULL)
  m_pszAppName = _tcsdup(lpszAppName); // 为lpszAppName分配内存
 else
  m_pszAppName = NULL;

 // initialize CWinThread state
 AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
 ENSURE(pModuleState);
 AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
 ENSURE(pThreadState);
 ASSERT(AfxGetThread() == NULL);
 pThreadState->m_pCurrentWinThread = this;  // 如果有子类继承了CWinApp, this就是子类
 ASSERT(AfxGetThread() == this);
 m_hThread = ::GetCurrentThread();
 m_nThreadID = ::GetCurrentThreadId();

 // initialize CWinApp state
 ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
 pModuleState->m_pCurrentWinApp = this;   // 如果有子类继承了CWinApp, this就是子类
 ASSERT(AfxGetApp() == this);

 // in non-running state until WinMain
 m_hInstance = NULL;
 m_hLangResourceDLL = NULL;v
 m_pszHelpFilePath = NULL;
 m_pszProfileName = NULL;
 m_pszRegistryKey = NULL;
 m_pszExeName = NULL;
 m_pRecentFileList = NULL;
 m_pDocManager = NULL;
 m_atomApp = m_atomSystemTopic = NULL;
 m_lpCmdLine = NULL;
 m_pCmdInfo = NULL;

 // initialize wait cursor state
 m_nWaitCursorCount = 0;
 m_hcurWaitCursorRestore = NULL;

 // initialize current printer state
 m_hDevMode = NULL;
 m_hDevNames = NULL;
 m_nNumPreviewPages = 0;     // not specified (defaults to 1)

 // initialize DAO state
 m_lpfnDaoTerm = NULL;   // will be set if AfxDaoInit called

 // other initialization
 m_bHelpMode = FALSE;
 m_eHelpType = afxWinHelp;
 m_nSafetyPoolSize = 512;        // default size
}

然后是CTestApp的构造函数的调用。
在CTestApp的声明中,它重写了InitInstance函数,如下:
BOOL CTestApp::InitInstance()
{
 AfxEnableControlContainer();  //Call this function in your application object's InitInstance function
          //to enable support for containment of ActiveX controls

 // Standard initialization
 // If you are not using these features and wish to reduce the size
 //  of your final executable, you should remove from the following
 //  the specific initialization routines you do not need.

// In MFC 5.0, Enable3dControls and Enable3dControlsStatic are obsolete because their functionality is incorporated
// into Microsoft's 32-bit and 64-bit operating systems.  
 
#ifdef _AFXDLL
 Enable3dControls();   // Call this when using MFC in a shared DLL
#else
 Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

 // Change the registry key under which our settings are stored.
 // TODO: You should modify this string to be something appropriate
 // such as the name of your company or organization.
 SetRegistryKey(_T("Local AppWizard-Generated Applications"));

 LoadStdProfileSettings();  // Load standard INI file options (including MRU)

 // Register the application's document templates.  Document templates
 //  serve as the connection between documents, frame windows and views.

 CSingleDocTemplate* pDocTemplate;  // 单文档程序的模板生成
 pDocTemplate = new CSingleDocTemplate(
  IDR_MAINFRAME,
  RUNTIME_CLASS(CTestDoc),
  RUNTIME_CLASS(CMainFrame),       // main SDI frame window
  RUNTIME_CLASS(CTestView));
 AddDocTemplate(pDocTemplate);

 // Parse command line for standard shell commands, DDE, file open
 CCommandLineInfo cmdInfo;   // 对命令查询分析
 ParseCommandLine(cmdInfo);

 // Dispatch commands specified on the command line
 if (!ProcessShellCommand(cmdInfo))
  return FALSE;

 // The one and only window has been initialized, so show and update it.
 m_pMainWnd->ShowWindow(SW_SHOW);  //显示和更新窗口
 m_pMainWnd->UpdateWindow();

 return TRUE;
}


有了WinMain函数,也找到了显示和更新窗口的语句,但是从哪里开始设计窗口,注册窗口,建立窗口呢?
我又搜索了WNDCLASS,在wincore.cpp的4495行发现了与设计窗口时很像的函数BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
发现MS已经在里面为我注册了一些窗口,我只要选择自己想要的样式就可以了。

那么如何建立一个窗口呢?我又搜索了CreateWindow,在wincore.cpp的675行中有个BOOL CWnd::CreateEx函数。
里面有调用CreateWindowEx。这个函数还调用了一个叫PreCreateWindow的函数,这个函数主要是确定在建立窗口之前,确保要建立的窗口已经注册了。

好了,一切都准备好了。最后就是进入消息循环。

posted @ 2009-03-03 01:24 wil 阅读(1795) | 评论 (0)编辑 收藏

2009年3月2日

解析#pragma指令

转自CSDN

在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
其格式一般为: #Pragma Para
其中Para 为参数,下面来看一些常用的参数。

(1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗
口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:
#Pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_
X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了


(2)另一个使用得比较多的pragma参数是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。

(3)#pragma once (比较常用)
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。

(4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。

(5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体
外观的定义。

(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等价于:
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
#pragma warning(once:4385) // 4385号警告信息仅报告一次
#pragma warning(error:164) // 把164号警告信息作为一个错误。
同时这个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1---4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
常用的lib关键字,可以帮我们连入一个库文件。

posted @ 2009-03-02 18:59 wil 阅读(280) | 评论 (0)编辑 收藏

2009年2月3日

关于23种设计模式的有趣见解(转)

关于23种设计模式的有趣见解
        创建型模式 
        
        1、FACTORY—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory 
        
        工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。 
        
        2、BUILDER—MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你”builder。(这一定比美军在伊拉克用的翻译机好卖) 
        
        建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 
        
        3、FACTORY METHOD—请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。 
        
        工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。 
        
        4、PROTOTYPE—跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。(100块钱一份,你要不要) 
        
        原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。 
        
        5、SINGLETON—俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事) 
        
        单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。 
        
        结构型模式 
        
        6、ADAPTER—在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我) 
        
        适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。 
        
        7、BRIDGE—早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了 
        
        桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。 
        
        8、COMPOSITE—Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM都会用Composite模式了,你会了没有? 
        
        合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。 
        
        9、DECORATOR—Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗? 
        
        装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。 
        
        10、FACADE—我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。 
        
        门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。 
        
        11、FLYWEIGHT—每天跟MM发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。 
        
        享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。 
        
        12、PROXY—跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自动的回答,接收到其他的话时再通知我回答,怎么样,酷吧。 
        
        代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。 
        
        行为模式 
        
        13、CHAIN OF RESPONSIBLEITY—晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑! 
        
        责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接 
        
        起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。 
        
        14、COMMAND—俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送COMMAND,就数你最小气,才请我吃面。”,:-( 
        
        命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。 
        
        15、INTERPRETER—俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要做一个Interpreter,照着上面的脚本执行就可以了。 
        
        解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。 
        
        
        
        16、ITERATOR—我爱上了Mary,不顾一切的向她求婚。 
        
        Mary:“想要我跟你结婚,得答应我的条件” 
        
        我:“什么条件我都答应,你说吧” 
        
        Mary:“我看上了那个一克拉的钻石” 
        
        我:“我买,我买,还有吗?” 
        
        Mary:“我看上了湖边的那栋别墅” 
        
        我:“我买,我买,还有吗?” 
        
        Mary:“你的小弟弟必须要有50cm长” 
        
        我脑袋嗡的一声,坐在椅子上,一咬牙:“我剪,我剪,还有吗?” 
        
        …… 
        
        迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。 
        
        17、MEDIATOR—四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话。 
        
        调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。 
        
        18、MEMENTO—同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。 
        
        备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。 
        
        19、OBSERVER—想知道咱们公司最新MM情报吗?加入公司的MM情报邮件组就行了,tom负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦 
        
        观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。 
        
        20、STATE—跟MM交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的MM就会说“有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过可以带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把MM的状态从不讨厌不喜欢变成喜欢哦。 
        
        状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。 
        
        21、STRATEGY—跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到MM的芳心,我的追MM锦囊中有好多Strategy哦。 
        
        策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。 
        
        22、TEMPLATE METHOD——看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现); 
        
        模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。 
        
        23、VISITOR—情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了; 
        
        访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。
 

=====================================================================
另一种解释

==创建型模式==

1、  =SIMPLE FACTORY=
打完篮球真累,正好边上有个小摊。
“来杯可乐。”
“我要芬达。”
“一瓶矿泉水。”

工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。有了小摊这个工厂,我们口渴的问题就很easy的解决了。

2、 =FACTORY METHOD=
以前每次下午打完篮球后一般很晚,回来再洗个澡,食堂就关门了。我们就集体跑过西三门外吃牛肉面(呵呵,人生之一大爽事啊),每个餐厅的风味还不一样,这无所谓啦,我们只要说一句“来碗牛肉面“就行了。

工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。每一个餐厅就是一个具体的工厂,可惜现在西三门已经关掉了,郁闷ing!

3、 =SINGLETON=
Kobe就是Kobe,不管你是从电视上看到,还是从报纸上看到,其实就是他一个人

单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。组织后卫可以有几个,但Kobe只能有一个,废话!

4、 =BUILDER=
NBA中强队颇多,且各有自己的特点,因此对付不同的队有不同的打法,但你只要说“今天打国王”就行了,具体该怎么打由教练去安排(build)就行了。

建造模式:将产品的内部表象和客户端分来,客户不必知道产品内部组成的细节,因此当产品的表象一般很复杂时才用。战术安排的确是个比较专业的任务,所以…。

5、 =PROTOTYPE=
今年全明星赛真不错,真想再看一遍。
“小陈,把serv-u开一下,我下你的全明星赛。”
“OK!不过先上传两部好片。”
“啊,我晕~~!”

原始模型模式:实际上就是复制啦。原始模型模式允许动态的增加或减少产品类,产品类适合于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。还好,Windows里面的东东只要点右键,都有个复制选项。

==结构型模式==

6、 =ADAPTER=
姚明刚去火箭时,交流有点不便,但通过经纪人Adapter,姚明很快就和火箭的其他人混熟了。

适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。通过经纪人Adapter,主教练就可以把姚明看作本土人(会说e文的人)。如今姚明已经加强了功能,使得不要经纪人也可以和主教练交流,呵呵,str man!

7、 =COMPOSITE=
上半场被灌了个50:25,趁中场暂停,大家一起来安排下半场怎么打:
“方案A:6号太准,要专人盯防。”―“就是就是!”
“方案B:左边防守太弱,把XX换上来。”-“好耶好耶!”
“方案C:进攻太差,多打一些挡差。”-“不错不错!”
“方案D:上半场方案X其实还是不错的,下半场go on。”-“OKOK!“

一声哨响,下半场开始,我们把方案A,B,C…结合,定为方案Y。@#¥%^&*(!~等一系列后,我们终于以51:50战胜对手,哈哈…!!白日梦#&@*!

合成模式:合成模式使得客户端把单独的成分对象和由他们复合而成的合成对象同等看待,因此合成模式使得客户端增加新的构件变得容易。方案A是一条简单的方案,方案D是由不同的方案结合而成的复杂方案,但我们不管这些,我们只知道它们都是我们所要的方案。

8、 =DECORATOR=
记得西边操场没修好时,我们踢球没有地盘,还好,有个篮球场是空的,我们便捡来几块砖头,摆上两个门,哈哈,这样,篮球场也就变成小足球场了。
系里举办一个舞会,得找块大点的地盘,我们又看中了篮球场,挂起一盏灯,搬来两个音箱,ok,一切搞定。这样,篮球场便变成了舞会厅了,哈哈。

装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。其实,篮球场还可以变成很多其它的东东,只要发挥你的想像,嘻嘻。

9、 =PROXY=
玩NBA正happying。
突然,小付跑过来说:“你的电话,X系说明天下午两点要跟俺们系干一场,怎么样?”
-“OK,就跟他们说没问题!”玩Games要紧。
小付作为一个代理倒省了俺不少事,呵呵。

    代理模式:代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。X系只知道我们同意和他们干一场,但并不知道是回答他们的就是小付。

10、 =FLYWEIGHT=
 上次说到吃牛肉面,我们当中有几个还特挑剔。这不:
“老板,我要麻辣的。”-“好咧!”唰唰,老板放了些辣酱。
“我要川味的。”-“好咧!”唰唰,老板放了些泡菜。
“我也要川味的。”-老板按刚才的样式又做了一遍。
小陈一看大家都要,不服,就说:“老板,俺要黑的。”
“黑的?黑的是什么样的。”老板纳闷了。
“黑的,黑的就是多放些酱油…”-全场狂晕#&*%

    享元模式:享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是其状态存储在享元内部,不会随环境的改变而有所不同。将可以共享的状态和不可以共享的状态从常规类中区分开来,把不可以共享的状态从类里剔除出去。客户端从工厂中获得对象时,工厂会先检查其是否有该对象,如果有则直接返回给客户,没有才创建新的实例。享元模式大幅度的降低内存中对象的数量。那些麻辣啦,川味啦,都是享元,要的牛肉面有很多,但口味却就那么几种。后来,我们打玩篮球又去了那家店,还没等过小陈开口,老板就说:“这位同学,你是要黑牛肉面吧。”-小陈“……”

11、=FACADE=
又要和X系开始一季一度的比赛了,具体时间和地点还得靠俺这个队长来搞定,好,no problem!给X系篮球队队长一个call:“星期六下午3:00,我们一见高下。”

    门面模式:门面模式提供一个高层次的接口,使得子系统更易于使用,它将客户端和一些子系统分离,提高子系统的独立性和可移植性。X系篮球队队长就是个Facade,通过他,免得我要去跟他们系队员(子系统)一个个去通知。

12、=BRIDGE=
我是个学生,但在篮球队里,我又是个队员,那你说我是什么,超级赛亚人?呵呵。

桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。哈哈,多亏这个模式,不然我还真不知道该怎么称呼我自己呢,嘻嘻。

==行为模式==

13、=STRATEGY=
足球里有很多战术,比如,一般情况用442,打强队可以用451,打弱队可以用343。
当然篮球里面也有的,比如,你可以选择打中锋,或者远投,当然我们平时最多用的可能就是独干,哈哈。

    策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。

14、=TEMPLATE METHOD=
不同的篮球班有不同的老师,但他们上课的内容都是一样的,一般都分为运球,传球,投篮,上篮这么几堂课,估计体育师范学院教书的模版就是这样。具体运球怎么教,上篮怎么教就依不同的老师自己了。

    模板方法模式:模板方法模准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

15、=OBSERVER= 
“今天下午去打篮球啊。”-A
“好啊,到时叫上我。” -B
“也叫上我。” -C
“还有我。”-D

我们班的号召力可见一斑,呵呵。

    观察者模式:观察者模式使观察者和被观察者之间的耦合抽象化,被观察者会向所有的登记过的观察者发出通知。当A要去打篮球时,便会通知B,C,D…

16、=ITERATOR=
大一时上篮球课,体育老师要清名单:“小王(体育委员),你们班到了多少人?”
小王:“立正--,报数。”
“1,2,3,4,…”

    迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。小王真聪明,不必查询任何一个人就知道到了多少人。

17、=CHAIN OF RESPONSIBLEITY=
现在湖人队进攻,佩顿将球交给马龙,马龙再传给奥尼尔,奥尼尔一记重扣,湖人再添2分。

    责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用连接起来而形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。其实刚才那个球,奥尼尔可以再传给科比来个空中接力大风车灌篮的,那就PERFECT了!

18、=COMMAND=
一场关键比赛最后一秒由俺的三分决定了胜负,不过,可累坏了俺。
“来杯水。”真听话,我同学A马上叫一靓MM给我倒了杯水。
“背好酸。”真听话,我同学A马上又叫那个靓MM给我锤背。
“真想洗个澡。”真听话…“等等,我不是那个意思,哇哇…^¥&#*。”

    命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。如果早知道那个命令怎么执行,俺就不会说那句话了,呜呜…

19、=MEMENTO=
下了一个NBA小游戏,里面可以自动调关,但要改注册表,当然,先找到键值“总决赛”,右键,导出为final.reg,剩下来你就可以随便改了,万一改错了,还可以双击final.reg,呵呵。

    备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并与该对象分离,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

20、STATE 
今天打球真没劲,投篮老委掉。
忽然,一靓MM走过来… 啊--,超级赛亚人第三阶,变身!
变身之后果然不同凡响,轻松幌过两个人,在三个人的夹击下出手命中,同时加罚!(呵呵,就会吹)

    状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去像是改变了它的类一样。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统实际改变所选的子类。当一个对象行为依赖其所处状态时,就可以考虑用该模式。靓MM没来时,状态差,导致投篮委。靓MM一来,状态奇佳,就是打手也能进,哈哈。

21、=VISITOR=
下学期篮球课换了个老师,当然,他对我们全然不熟,于是第一节课他拿着花名册找到体育委员小王:“小陈技术怎样?”-“一般般,”
“小罗呢?”-“还可以,不过上篮不行。”
“小何呢?”-“他啊…”
等等,俺赶紧咳嗽一声。
“他怎么样?”-“他,他不错不错!” 哈哈。
“就是投篮老委!”-啊,你这小样,耍我。

    访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,使得增加新的操作很容易,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变,比如,老师还可以通过小王得到我们每个人的身高等。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类(比迭代子强),但增加一个新的节点就变得很困难,如果我们班来了个新同学,小王就没那么管用了。 

22、=INTERPRETER=
想知道怎么玩出酷呆了的假动作么,我这儿有AND1的经典集锦,按照上面练习就行了。

    解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。

23、=MEDIATOR=
俺一高中同学小D到我这儿来玩,当然,打球是必不可少的。叫上小王就出去K别人,但小D和小王不熟,所以几乎没什么配合,结果被CAI了个5:0。只好下场“休息”。
小王:“叫你那个同学少单干,多传球。”-“OK。”我过去把意见传给了小D。
小D:“你那个同学防守有问题,要盯人,不要盯球。”-“NO PROBLEM。”我又过去把意见传给了小王。

    调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。
果然,依靠我这个MEDIATOR,我们马上还了对方一个鸡蛋。哈哈!

posted @ 2009-02-03 13:25 wil 阅读(236) | 评论 (0)编辑 收藏

2009年1月25日

新年愿望

(1)找到理想的工作
(2)每天能看1小时的书
(3)下年这时候能给爸妈孝顺钱过年

posted @ 2009-01-25 23:58 wil 阅读(141) | 评论 (0)编辑 收藏

仅列出标题  下一页
<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

导航

统计

常用链接

留言簿(1)

随笔分类

随笔档案

文章分类

搜索

最新评论

阅读排行榜

评论排行榜