1、程序的诞生
■ Application object 产生,内存于是获得配置,初值亦设立了。
■ Afx WinMain 执行AfxWinInit,后者又调用AfxInitThread,把消息队列尽量加大到
96。
■ Afx WinMain 执行InitApplication。这是CWinApp 的虚拟函数,但我们通常不改
写它。
■ AfxWinMain 执行InitInstance。这是CWinApp 的虚拟函数,我们必须改写它。
■ CMyWinApp::InitInstance 'new' 了一个CMyFrameWnd 对象。
■ CMyFrameWnd 构造式调用Create,产生主窗口。我们在Create 参数中指定的
窗口类别是NULL, 于是MFC 根据窗口种类, 自行为我们注册一个名为
"AfxFrameOrView42d" 的窗口类别。
■ 回到InitInstance 中继续执行ShowWindow,显示窗口。
■ 执行UpdateWindow,于是发出WM_PAINT。
■ 回到AfxWinMain,执行Run,进入消息循环。
2、程序开始运作
■ 程序获得WM_PAINT 消息(藉由CWinApp::Run 中的::GetMessage 循环)。
■ WM_PAINT 经由::DispatchMessage 送到窗口函数CWnd::DefWindowProc 中。
■ CWnd::DefWindowProc 将消息绕行过消息映射表格(Message Map)。
■ 绕行过程中发现有吻合项目,于是调用项目中对应的函数。此函数是应用程序
利用BEGIN_MESSAGE_MAP 和END_MESSAGE_MAP 之间的宏设立起来的。
■ 标准消息的处理例程亦有标准命名,例如WM_PAINT 必然由OnPaint 处理。
3、程序的死亡
■ 使用者选按【File/Close】,于是发出WM_CLOSE。
■ CMyFrameWnd 并没有设置WM_CLOSE 处理例程,于是交给预设之处理例程。
■ 预设函数对于WM_CLOSE 的处理方式是调用::DestroyWindow, 并因而发出
WM_DESTROY。
■ 预设之WM_DESTROY 处理方式是调用::PostQuitMessage,因此发出WM_QUIT。
■ CWinApp::Run 收到WM_QUIT 后会结束其内部之消息循环, 然后调用
ExitInstance,这是CWinApp 的一个虚拟函数。
■ 如果CMyWinApp 改写了ExitInstance , 那么CWinApp::Run 所调用的就是
CMyWinApp::ExitInstance,否则就是CWinApp::ExitInstance。
■ 最后回到AfxWinMain,执行AfxWinTerm,结束程序。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/civita/archive/2008/10/12/3061677.aspx
1、_T宏、UNICODE和ANSI
windows的API大多有两套函数,比如TextOut,系统中实际的函数是TextOutA()和TextOutW()两个函数。若在编译时使用了_UNICODE宏,则用TextoutW()函数,否则为TextOutA()函数。
同样,字符串也有Unicode串和ANSI串之分。
在字符创之前使用宏_T("***"),若编译时使用了_UNICODE宏,则为UNICODE字符串,否则为ANSI字符串。_T即为_TEXT,可见tchar.h。
_T 告诉编辑器:如果定义了_UNICODE,其中的字符串是按UNICODE编码;否则其中的字符串就按ANSI编码。
_T的用法是:_T("字符串")。
2、typedef和define
2者的定义形式为:
#define NEW( OLD)
typedef OLD NEW;
typedef 表达式(NEW); //用NEW代表一个"表达式()"的类型
为了尽可能地兼容,一般都遵循#define定义“可读”的常量以及一些宏语句的任务,而typedef则常用来定义关键字、冗长的类型的别名。
注意以下2种形式意义不全相同:
typedef (int*) pINT;
#define pINT2 int*
效果不同!实践中见差别:pINT a,b;的效果同int *a; int *b;表示定义了两个整型指针变量。而pINT2 a,b;的效果同int *a, b;
3、PeekMessage和GetMessage
两个函数主要有以下两个区别:
1.GetMessage将等到有合适的消息时才返回,而PeekMessage只是撇一下消息队列。
2.GetMessage会将消息从队列中删除,而PeekMessage可以设置最后一个参数wRemoveMsg来决定是否将消息保留在队列中。
在Windows的内部,GetMessage和PeekMessage执行着相同的代码。而两者最大的不同之处则体现在没有任何消息返回到应用程序的情况下。在此种情况下,PeekMessage会返回一个空值到应用程序,GetMessage会在此时让应用程序休眠。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/civita/archive/2008/10/13/3061684.aspx
1、type-safe
type safe(类型安全)就是判断是否对正确的经过授权的内存进行访问。
"正确的"是说:比如说做类型转换,
Class1 * p1=new Class1();
Class2 * p2=(Class1 *)p1;
p2->MethodofClass1();这个时候就有可能出问题了。
可以尽量使用static_cast进行类型转换,比如说
Class2 * p2=static_cast<Class1 *>(p1);
如果转换的类型并不对应实际的基类,那么static_cast版本将会引发一个编译错误,而不是运行时错误。
"经过授权"是说:这块内存是自己的进程的可以访问的,比如说强行给一个指针赋值一个内存地址,然后强行写数据到这块内存会引发很多不安全因素,为了防止这种事情,操作系统可以检查你是否有足够的权限访问不是自己进程空间里面的内存区域(比如说操作系统的保留内存区域)。
2、C++ Template
"templates are C++ language-specific and are handled by the compiler (rather than the preprocessor) and so are type-safe."
复制一段既有程序代码的一个最平常的理由就是为了改变数据类型。
宏有它自己的缺点。第一,它只适用于简单的功能。第二个缺点比较严重:宏不提供资料型别检验,因此牺牲了C++ 的一个主要效益。第三个缺点是:宏并非函数。
C++ 提供给我们一个更好的繁殖方法:template。Templates 提供比较好的解决方案,它把「一般性的算法」和其「对资料型别的实作部份」区分开来。可以先写算法的程序代码,稍后再使用时再填入实际资料型别。新的C++ 语法使「资料型别」也以参数的姿态出现。
C++ 的template 有两种,一种针对function,另一种针对class。
A.template function
template <class T> //T作为类型的别名,在编译时能根据实际的使用场景的类型进行型别输入和判断,具有宏的代码简化功效
T power(T base, int exponent);//T在定义template的时候并没有固定类型,只有在程序代码各处实际使用此template函数的时候才确定,所以又具有重载函数的性质
B.template class
#0001 template <class T>
#0002 class CThree
#0003 {
#0004 public :
#0005 CThree(T t1, T t2, T t3);
#0006 T Min();
#0007 T Max();
#0008 private:
#0009 T a, b, c;
#0010 };
定义template类的成员函数时候,都要加上template <class T>,而且类别名称应该使用CThree<T>。
MFC 的collection classes 里头有一些是template-based,对于类型检验的功夫做得比较好。这些类别区分为:
■ 简单型- CArray、CList、CMap。它们都衍生自CObject,所以它们都具备了文件读写、执行时期型别鉴识、动态生成等性质。
■ 类型指针型- CTypedPtrArray、CTypedPtrList、CTypedPtrMap。这些类别要求你在参数中指定基础类别, 而基础类别必须是MFC 之中的non-template pointer collections,例如CObList 或CPtrArray。你的新类别将继承基础类别的所有性质。
可以把template看成是一个type checking的micro
可以认为编译器在遇到template的时候,把实际的类型代入做宏展开来保证type-safe。
比如说下面定义一个template
#include <iostream>
template <class A_Type>
int find( A_Type * array, A_Type value, int size )
{
for ( int i = 0 ; i < size ; i++ )
if ( array[i] == value ) return i ;
return -1 ;
}
下面的测试代码
int intArray[] = { 1, 3, 5, 7 } ;
float f = 5.0 ;
int index = find( intArray, f, 4 ) ;
是会编译出错的,因为它做了type checking。但macros没法定义变量类型的,当然编译器也无法
检查是否是正确的类型了。
xb
补充:micro就是一种preprocessor,而template不是
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/civita/archive/2008/10/12/3064226.aspx
1、Persistence的实现方法
MFC 有一套Serialize 机制,目的在于把档名的选择、文件的开关、缓冲区的建立、资料的读写、萃取运算子(>>)和嵌入运算子(<<)的多载(overload)、对象的动态生成都包装起来。
2、Serialize的目标
如下图:
我们可以在每次记录对象内容的时候,先写入一个代码,表示此对象之类别是否曾在档案中记录过了。如果是新类别,乖乖地记录其类别名称;如果是旧类别,则以代码表示。这样可以节省文件大小以及程序用于解析的时间。啊,不要看到文件大小就想到硬盘很便宜,桌上的一切都将被带到网上,你得想想网络频宽这回事。
3、Archive
每一个可写到文件或可从文件中读出的类别,都应该有它自己的Serailize 函数,负责它自己的资料读写文件动作。此类别并且应该改写<< 运算子和>> 运算子,把资料导流到archive 中。
archive 是什么?是一个与文件息息相关的缓冲区,暂时你可以想象它就是文件的化身。
4、DECLARE_SERIAL / IMPLEMENT_SERIAL 宏
要将<< 和>> 两个运算子多载化,还要让Serialize 函数神不知鬼不觉地放入类别声明之中,最好的作法仍然是使用宏。
操作符<<和>>的格式是operator>>(流,输入/输出的类型);所以对操作符重载,可以使其直接用于用户定义的结构体/类进行输入、输出(改变输入/输出的类型)或重定向(改变流类型、API)操作。
#define DECLARE_SERIAL(class_name) /
DECLARE_DYNCREATE(class_name) / //这个我们在“动态创建”中见过
friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb);
//如果某一个函数定义为类的友元,则该函数就可以访问该类的私有成员;在具有Serialize功能的类中重载operator>>而不重载<<的原因:这2个重载函数皆被定义为全局函数,而(ReadObject)需要具有对类的CRuntimeClass的权限,因为在读完档后还要做动态生成,故需要将此函数定位class_name类的友元并且要在class_name类中重载,而(WriteObject)并不需要CRuntimeClass
//关于operator的重载,这里需要说明的是:operator本身是一个全局函数,如果某类将operator作为成员,会在调用operator的时候,将类自动作为左变量,而导致与预期的结果不符,要将类作为右变量处理,则需要在重载前面加上friend关键词,以示将操作符不当作自己的成员调用
//CArchive类中也重载了上述operator>>,还有<<
//CArchive& AFXAPI operator>>(CArchive& ar,CObject*& pOb)
#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) /
CObject* PASCAL class_name::CreateObject() / //这些功能是要基于动态创建的
{ return new class_name; } /
_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, /
class_name::CreateObject) /
CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) /
{ pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); /
return ar; } /
在每一个对象被处理(读或写)之前,能够处理琐屑的工作,诸如判断是否第一次出现、记录版本号码、记录文件名等工作,CRuntimeClass 需要两个函数Load 和Store。
struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema; // schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
CRuntimeClass* m_pBaseClass;
CObject* CreateObject();
void Store(CArchive& ar) const;
//将类别属性存入文件(ar)中
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);
//将CRuntimeClass链表中没有的新类别从文件(ar)中读出
// CRuntimeClass objects linked together in simple list
static CRuntimeClass* pFirstClass; // start of class list
CRuntimeClass* m_pNextClass; // linked list of registered classes
};
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/civita/archive/2008/10/13/3070085.aspx
如果一个变量你需要几种可能存在的值,那么就可以被定义成为枚举类型。之所以叫枚举就是说将变量或者叫对象可能存在的情况也可以说是可能的值一一例举出来。
举个例子来说明一吧,为了让大家更明白一点,比如一个铅笔盒中有一支笔,但在没有打开之前你并不知道它是什么笔,可能是铅笔也可能是钢笔,这里有两种可能,那么你就可以定义一个枚举类型来表示它!
enum box{pencil,pen};//这里你就定义了一个枚举类型的变量叫box,这个枚举变量内含有两个元素也称枚举元素在这里是pencil和pen,分别表示铅笔和钢笔。
这里要说一下,如果你想定义两个具有同样特性枚举类型的变量那么你可以用如下的两种方式进行定义!
enum box{pencil,pen};
enum box box2;//或者简写成box box2;
再有一种就是在声明的时候同时定义。
enum {pencil,pen}box,box2; //在声明的同时进行定义!
枚举变量中的枚举元素系统是按照常量来处理的,故叫枚举常量,他们是不能进行普通的算术赋值的,(pencil=1;)这样的写发是错误的,但是你可以在声明的时候进行赋值操作!
enum box{pencil=1,pen=2};
但是这里要特别注意的一点是,如果你不进行元素赋值操作那么元素将会被系统自动从0开始自动递增的进行赋值操作,说到自动赋值,如果你只定义了第一个那么系统将对下一个元素进行前一个元素的值加1操作,例如
enum box{pencil=3,pen};//这里pen就是4系统将自动进行pen=4的定义赋值操作!
前面说了那么多,下面给出一个完整的例子大家可以通过以下的代码的学习进行更完整的学习!
#include <iostream>
using namespace std;
void main(void)
{
enum egg {a,b,c};
enum egg test; //在这里你可以简写成egg test;
test = c; //对枚举变量test进行赋予元素操作,这里之所以叫赋元素操作不叫赋值操作就是为了让大家明白枚举变量是不能直接赋予算数值的,例如(test=1;)这样的操作都是不被编译器所接受的,正确的方式是先进行强制类型转换例如(test = (enum egg) 0;)!
if (test==c)
{
cout <<"枚举变量判断:test枚举对应的枚举元素是c" << endl;
}
if (test==2)
{
cout <<"枚举变量判断:test枚举元素的值是2" << endl;
}
cout << a << "|" << b << "|" << test <<endl;
test = (enum egg) 0; //强制类型转换
cout << "枚举变量test值改变为:" << test <<endl;
cin.get();
}
看到这里要最后说一个问题,就是枚举变量中的枚举元素(或者叫枚举常量)在特殊情况下是会被自动提升为算术类型的!
#include <iostream>
using namespace std;
void main(void)
{
enum test {a,b};
int c=1+b; //自动提升为算术类型
cout << c <<endl;
cin.get();
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/civita/archive/2008/10/17/3091190.aspx