MFC源码剖析之——MFC来龙去脉

MFC源码剖析之——MFC来龙去脉.txt熬夜,是因为没有勇气结束这一天;赖床,是因为没有勇气开始这一天。朋友,就是将你看透了还能喜欢你的人。MFC源码剖析之——MFC来龙去脉

以传统的C/SDK 撰写Windows 程序,最大的好处是可以清楚看见整个程序的来龙去脉

和消息动向,然而这些重要的动线在MFC 应用程序中却隐晦不明,因为它们被Application Framework 包起来了。这一章主要目的除了解释MFC 应用程序的长像,也要从MFC 源代

码中检验出一个Windows 程序原本该有的程序进入点(WinMain)、视窗类别注册(RegisterClass)、窗口产生(CreateWindow)、消息循环(Message Loop)、窗口函数(WindowProcedure)等等动作,抽丝剥茧彻底了解一个MFC 程序的诞生与结束,以及生

命过程。

先来看个一般的C/SDK Windows程序:

//////////////////////////////////////////////////////////////

// Generic.cpp 一般的windows程序流程演示

#include

LRESULT CALLBACK MyWinProc(HWND,UINT,WPARAM,LPARAM);//○6

int WINAPI WinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nShowCmd)

{

WNDCLASS wc;

wc.cbClsExtra=0;

wc.cbWndExtra=0;

wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

wc.hCursor=LoadCursor(NULL,IDC_ARROW);

wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);

wc.hInstance=hInstance;

wc.lpfnWndProc=MyWinProc;

wc.lpszClassName="generic";

wc.lpszMenuName=NULL;

wc.style=CS_VREDRAW | CS_HREDRAW;//窗口的样式

RegisterClass(&wc);//注册窗口类○1

HWND hWnd;

hWnd=CreateWindow(

"generic",

"Title",

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

NULL,

NULL,

hInstance,

NULL);//产生窗口○2

ShowWindow(hWnd,SW_NORMAL);//显示窗口○3

UpdateWindow(hWnd);//更新窗口○4

MSG msg;

while(GetMessage(&msg,NULL,0,0)){

TranslateMessage(&msg);

DispatchMessage(&msg);

}//消息循环○5

return 0;

}

LRESULT CALLBACK MyWinProc(

HWND hwnd,//窗口句柄

UINT uMSG,//消息标志符

WPARAM wParam,//消息的第一个参数

LPARAM lParam//消息的第二个参数

)

{

switch(uMSG){

case WM_LBUTTONDOWN://鼠标左键点击

MessageBox(hwnd,"点击","HelloWorld",MB_OK);

break;

case WM_CLOSE://窗口被关闭消息

if(IDYES==MessageBox(hwnd,"是否真的要关闭窗口?","提示",MB_YESNO)){ DestroyWindow(hwnd);

}

break;

case WM_PAINT://窗口被绘制时消息

HDC hDc;

PAINTSTRUCT ps;

hDc=BeginPaint(hwnd,&ps);

TextOut(hDc,0,10,"第一行字符",10);

EndPaint(hwnd,&ps);

break;

case WM_DESTROY://窗口被销毁是消息

PostQuitMessage(0);//有了这个才可以使while(GetMessage(&MSG, , ,))为假

break;

default://处理我不感兴趣的消息

return DefWindowProc(hwnd,uMSG,wParam,lParam);

}

return 0;

}

从上述的原码中可以看出windows程序设计有个基本的脉络:Register Class(WNDCLASS*) →Create Window→Show Window→Update Window →Message Loop→Window procedure。但我们从MFC中看不出有这么一个流程,甚至是看不到程序的入口function named WinMain。

即便是基于MFC的应用程序,建立窗口类也是会遵循如下的过程:

设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口->消息循环->消息路由到窗口

过程函数处理。下面就剖析一下在MFC中是如何完成上述过程的。

(1)每个应用程序都有且仅有一个应用类的全局变量theApp,全局变量先于WinMain函数进

行处理。

(2)WinMain函数体在APPMODUL.CPP文件中,定义如下:

extern "C" int WINAPI

_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

// call shared/exported WinMain

return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

}

其中#define _tWinMain WinMain

(3)AfxWinMain函数体在WINMAIN.CPP文件中,里面有如下两句话:

CWinThread* pThread = AfxGetThread();

CWinApp* pApp = AfxGetApp();

其实这里得到的这两个指针都是指向全局的对象theApp的;

接下来有函数调用pThread->InitInstance(),根据多态性,会调用CXXXApp类中的InitInstance()函数。该函数很重要,在对该函数的调用中就会完成:设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口。

接下来,该函数中会继续调用pThread->Run(),这就完成了:消息循环->消息路由到窗口过程函数处理。

(4)进入CXXXApp::InitInstance()函数体中,对于单文档应用程序,调用ProcessShellCommand(cmdInfo),通过调用该函数就会完成:设计窗口类->注册窗口类-> 生成窗口。

再接下来就会调用m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();这就完成了:显示窗口->更新窗口。

(5)在函数CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)中会进入到如下的case分支:case CCommandLineInfo::FileNew:

if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))

(6)进入函数CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,

AFX_CMDHANDLERINFO* pHandlerInfo),调用_AfxDispatchCmdMsg(this, nID, nCode, lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);

(7)进入函数AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode, AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo),调用case AfxSig_vv:

// normal command or control notification

ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED

ASSERT(pExtra == NULL);

(pTarget->*mmf.pfn_COMMAND)();

(8)进入CWinApp::OnFileNew(),调用m_pDocManager->OnFileNew();这个函数很特殊,它本身是个消息响应函数,当我们点击ID为ID_FILE_NEW的菜单时,会产生一个命令消息,

于命令消息可以被CCmdTarget类及其派生类来捕获,而 CWinApp是从CCmdTarget派生出来的,

因此可以捕获这个消息。当应用程序创建完成并成功显示后,当我们点击文件菜单下的新建菜单项时,就会首先进入这个函数,然后再依次执行下去,最后就会执行到

pDocument->OnNewDocument()中,往往我们会对这个函数不解,不知道它为什么会响应

ID_FILE_NEW的命令消息,至此真相大白了。顺便说一句,为什么程序在刚启动的时候,我们并没有点击菜单项,为什么会自动的产生这个消息呢?这是因为在

CXXXXApp::InitInstance()函数中有“CCommandLineInfo cmdInfo;”这个类的构造函数是这样的:CCommandLineInfo::CCommandLineInfo()

{

m_bShowSplash = TRUE;

m_bRunEmbedded = FALSE;

m_bRunAutomated = FALSE;

m_nShellCommand = FileNew;

},因此就会在第(5)步骤的时候进入到“case CCommandLineInfo::FileNew:”这个分支中,就相当于产生了这样一个FileNew的消息。同理对于ID为 ID_FILE_OPEN(在CWinApp::OnFileOpen()中响应)、ID_FILE_SAVE(在 CDocument::OnFileSave()中响应)

等等在MFC向导为我们生成的单文档类中找不到消息响应的入口时,其实都是在基类CWinApp 或者CDocument类中进行了响应。对于CXXXXDoc::Serialize(CArchive& ar)函数也是通过ID_FILE_SAVE和ID_FILE_OPEN产生命令消息后就行响应从而才调用该函数的。

(9)进入CDocManager::OnFileNew(),CDocManager类有一个成员变量是CPtrList

m_templateList;该变量保存了一个文档模版链表指针,在CDocManager::OnFileNew()函数体中会调用 CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();得到链表中的头,也就是第一个文档模版,后面就会用得到的这个指针去调用

pTemplate->OpenDocumentFile(NULL);紧接着就会有一个判断,用来确定该链表中是否只有一项,如果链表中保存了多个文档模版,则会弹出一个对话框,来让我们选择到底是使用

哪一套文档模版来构建应用程序,相信大家也都见到过这种情况吧。对了,还有一点要说明的是:pTemplate是一个CDocTemplate的指针,但接下来程序为什么会进入到CSingleDocTemplate::OpenDocumentFile的函数体内呢,这是因为CDocTemplate类中的OpenDocumentFile函数被定义为纯虚函数,而CSingleDocTemplate类又是从CDocTemplate 类派生出来的,并且实现了该函数,因此就会进入到子类的函数体中了。

(10)进入CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible),先调用CreateNewDocument()创建文档类,再调用pFrame = CreateNewFrame(pDocument, NULL);创建框架类和视图类,从这里也可以看出MFC体系结构中文档、框架、视图“三位一体”的模式,在这一个函数中同时创建三个类;再会调用pDocument->OnNewDocument();因此就会进入到子类的文档类中的

pDocument->OnNewDocument()中了。

(11)进入CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther),调用if (!pFrame->LoadFrame(m_nIDResource,

WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, // default frame styles

NULL, &context))

(12)进入BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,

CWnd* pParentWnd, CCreateContext* pContext),调用

VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));

(13)进入BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister),该函数内部就完成了:设计窗口类->注册窗口类。MFC通过给我们提供好一些已经订制好的窗口类,我们不需要自己再设计窗口类,只需要到那些订制好的窗口类“仓库”中寻找一种适合我们需要的窗口类就可以了,然后通过AfxRegisterClass函数注册窗口类。还需要说明的是,再后续的跟踪过程中,我们会发现还会进入到AfxEndDeferRegisterClass函数中进行设计和注册窗口类,这主要是因为单文档应用程序比较特殊,它提前通过这样的一种途径进行了窗口类的设计和注册步骤,其实是应该在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) 函数的调用中完成窗口类的设计和注册的,这一点我们要清楚,也就是说设计和注册窗口类的正宗发源地应该是 PreCreateWindow(CREATESTRUCT& cs)。此外,我们还会注意到在该函数体的前部分有一语句为“wndcls.lpfnWndProc = DefWindowProc;”因此所有窗口类的窗口过程函数都是DefWindowProc,这一点在后面的跟踪中可以看到,每次生成窗口之后都会调用几次DefWindowProc函数。也就是说MFC都是让我们采用默认的窗口过程函数,这并不是说我们因此就不能使用自己的窗口过程函数实现个性化的消息处理了,MFC采用了一种基于消息映射的机制完成了消息个性化处理。

(14)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)中,调用LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);

(15)进入LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource),调用PreCreateWindow(cs);

(16)进入BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs),调用CFrameWnd::PreCreateWindow(cs)

(17)进入BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs),调用

VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));又一次设计和注册窗口类(18)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)中,调用if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext))

(19)进入BOOL CFrameWnd::Create(LPCTSTR lpszClassName,

LPCTSTR lpszWindowName,

DWORD dwStyle,

const RECT& rect,

CWnd* pParentWnd,

LPCTSTR lpszMenuName,

DWORD dwExStyle,

CCreateContext* pContext),调用if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,

rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))

(20)BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,

LPCTSTR lpszWindowName, DWORD dwStyle,

int x, int y, int nWidth, int nHeight,

HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),调用if (!PreCreateWindow(cs))

,接下来调用HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,

cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,

cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);好了,终于让我们找到

生成窗口的地方了——函数::CreateWindowEx!

(21)进入int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct),调用if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

(22)进入int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs),调用return OnCreateHelper(lpcs, pContext);

(23)进入int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (CWnd::OnCreate(lpcs) == -1)

(24)进入_AFXWIN_INLINE int CWnd::OnCreate(LPCREATESTRUCT),调用return

(int)Default();

(25)进入LRESULT CWnd::Default(),调用return

DefWindowProc(pThreadState->m_lastSentMsg.message,

pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam);

(26)进入LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),调用return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);

(27)回到int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (!OnCreateClient(lpcs, pContext))

(28)进入BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext),调用if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)

(29)进入CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID),调用if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext))

(30)进入BOOL CWnd::Create(LPCTSTR lpszClassName,

LPCTSTR lpszWindowName, DWORD dwStyle,

const RECT& rect,

CWnd* pParentWnd, UINT nID,

CCreateContext* pContext),调用return CreateEx(0, lpszClassName, lpszWindowName, dwStyle | WS_CHILD,

rect.left, rect.top,

rect.right - rect.left, rect.bottom - rect.top,

pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext);

(31)进入BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,

LPCTSTR lpszWindowName, DWORD dwStyle,

int x, int y, int nWidth, int nHeight,

HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),重复生成框架类CMainFrame的过程来生成CXXXView,因为它也是一个窗口类,因此也需要进行那一系列过程才能最终显示更新出来。

调用的顺序是这个样子的:PreCreateWindow(cs)->BOOL

CXXXView::PreCreateWindow(CREATESTRUCT&

cs)->CView::PreCreateWindow(cs)->VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIE W_REG));->::CreateWindowEx(...)->CWnd::DefWindowProc->::CallWindowProc(...)->.. .->CXXXView::OnCreate->CView::OnCreate->CWnd::OnCreate->...

写到这里,基本上就清楚了,中间的省略号表示的部分大多数都是在与窗口过程函数有关的,因为在生成窗口的时候需要响应一些消息,因此需要调用一些窗口过程函数,每次在调用::CreateWindowEx(...)函数后都会调用一些窗口过程函数,然后再去调用该窗口类对应的OnCreate函数,其实在调用OnCreate函数之前调用CreateWindowEx只是生成了一个窗口,

至于这个窗口里面要放置些什么东西,以及该如何装饰该窗口,则就需要由OnCreate来完成了,往往我们都会在OnCreate函数的后面(这样做是为了不影响窗口本身应该布置的格局)添加一些代码,创建我们自己的东西,比如我们通常会在CMainFrame类的OnCreate函数后面放置一些Create代码,来创建我们自己的可停靠的工具栏或者按钮之类的东西,当然我们也可以在CXXXView类的OnCreate函数的后面添加一些代码,来创建我们需要的东西,比如

按钮之类的东西。在完成了从设计、注册到生成窗口的过程之后,往往还需要显示更新,有些时候,我们不必要每次都显示的调用CWnd的ShowWindow和UpdateWindow两个函数,我们

可以在创建的时候,给窗口风格中添加WS_VISIBLE即可,因此有些时候会跟踪不到ShowWindow和UpdateWindow两个函数这两个函数,因为窗口在创建的时候就可见了。

总的来说,先初始化应用类,然后注册生成框架类,然后再注册生成视图类,然后注册

生成视图类OnCreate函数后面用户添加的、用Create来准备创建的窗口,然后再注册生成框架类的OnCreate函数后面需要生成的m_wndToolBar、m_wndStatusBar以及我们自己添加的

要创建的窗口类,最后在回到应用类的初始化的函数体中,调用框架类的显示和更新函数,然后再进入由框架类定义的窗口的消息循环中。

消息循环的过程是这个样子的:

(1)调用int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)函数中的pThread->Run()

(2)进入int CWinApp::Run(),调用return CWinThread::Run();

(3)进入int CWinThread::Run(),调用if (!PumpMessage())

(4)进入BOOL CWinThread::PumpMessage(),调用if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))

(5)回到BOOL CWinThread::PumpMessage(),调

用::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur);

(6)回到int CWinThread::Run(),调用while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

(7)再重复(4)-(6)的步骤

下面给出int CWinThread::Run()中消息循环的部分代码:

do

{

// pump message, but quit on WM_QUIT

if (!PumpMessage())

return ExitInstance();

// reset "no idle" state after pumping "normal" message

if (IsIdleMessage(&m_msgCur))

{

bIdle = TRUE;

lIdleCount = 0;

}

} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

这段代码其实本质上与我们基于Win32 SDK手写的代码:

//消息循环

MSG msg;

while(GetMessage(&msg,NULL,0,0))

{

//简单的说,函数TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻译成WM_CHAR消息,没

有该函数就不能产生WM_CHAR消息。

TranslateMessage(&msg);

::DispatchMessage(&msg);

}

是一致的。

Visual C++ MFC 中常用宏的含义

AND_CATCHAND_CATCH

AND_CATCH(exception_class,exception _object_point_name)

说明:

定义一个代码块,它用于获取废除当前TRY块中的附加异常类型。使用CATCH宏以获得一个异常类型,然后使用AND_CATCH宏获得随后的异常处理代码可以访问异常对象(若合适的话)已得到关于异常的特别原因的更多消息。在AND_CATCH块中调用THROW_LAST宏以便把处理过程移到下个外部异常框架。AND_CATCH可标记CATCH或AND_CATCH块的末尾。

注释:

AND_CATCH块被定义成为一个C++作用域(由花括号来描述)。若用户在此作用域定义变量,那么记住他们只在此作用域中可以访问。他也用于exception_object_pointer_name变量。ASSERT

ASSERT(booleanExpression)

说明:

计算变量的值。如果结构的值为0,那么此宏便打印一个诊断消息并且成讯运行失败。如果条件为非0,那么什么也不做。诊断消息的形式为: assertion failed in file in line 其中name

是元文件名,num是源文件中运行失败的中断号。在Release版中,ASSERT不计算表达式的值也就不中断程序。如果必须计算此表达式的值且不管环境如何那么用VERIFY代替ASSERT。注释:

ASSERT只能在Debug版中用

ASSERT_VAILD

ASSERT_VAILD(pObject)

说明:

用于检测关于对象的内部状态的有效性。ASSERT_VALID调用此对象的AssertValid成员函数

(把它们作为自己的变量来传递)。在 Release版中ASSERT_VALID什么也不做。在DEBUG版中,

他检查指针,以不同于NULL的方式进行检查,并调用对象自己的 AssertValid成员函数。

果这些检测中有任何一个失败的话,那么他会以与ASSERT相同的方法显示一个警告的消息。注释:

此函数只在DEBUG版中有效。

BEGIN_MESSAGE_MAP

BEGIN_MESSAGE_MAP(the class,baseclass)

说明:

使用BEGIN_MESSAGE_MAP开始用户消息映射的定义。在定义用户类函数的工具(.cpp)文

件中,以BEGIN_MESSAGE_MAP宏开始消息映射,然后为每个消息处理函数增加宏项,接着

以END_MESSAGE_MAP宏完成消息映射。

CATCH

CATCH(exception_class,exception_object_pointer_name)

说明:

使用此用定义一个代码块,此代码用来获取当前TRY块中都一个异常类型。异常处理代码可以访问异常对象,如何合适的话,就会得到关于异常的特殊原因的更多消息。调用THROW_LAST宏以把处理过程一下一个外部异常框架,如果exception‐class是类CExceptioon,

那么会获取所有异常类型。用户可以使用CObject::IsKindOf成员函数以确定那个特别异常被

排除。一种获取异常的最好方式是使用顺序的AND_CATCH语句,每个带一个不同的异常类型。此异常类型的指针由宏定义,用户不必定义。

注释:

此CATCH块被定义作一个C++范围(由花括号描述)。如用户在此范围定义变量,那么它们只在吃范围内可以访问。他还可以用于异常对象的指针名。

DEBUG_NEW

#define new DEBUG_NEW

说明:

帮助查找内存错误。用户在程序中使用DEBUG_NEW,用户通常使用new运算符来从堆上分配。在Debug模式下(但定义了一个DEBUG符号),DEBUG_NEW为它分配的每个对象记录文件名和行号。然后,在用户使用CMemoryState::DumpAllObjectSince成员函数时,每个以DEBUG_NEW分配的对象分配的地方显示出文件名和行号。为了使用DEBUG_NEW,应在用户

的资源文件中插入以下指令: #define new DEBUG_NEW 一旦用户插入本指令,预处理程序将在使用new的地方插入DEBUG_NEW,而MFC作其余的工作。但用户编译自己的程序的一个发行版时,DEBUG_NEW便进行简单的new操作,而且不产生文件名和行号消息。DECLARE_DYNAMIC

DECLARE_DYNAMIC(class_name)

说明:

但从CObject派生一个类时,此宏增加关于一个对象类的访问运行时间功能。把DECLARE_DYNAMIC宏加入类的头文件中,然后在全部需要访问词类对象的.CPP文件中都包含此模块。如果像所描述那样使用DELCARE_DYNAMIC和IMPLEMENT_DYNAMIC宏,那么用户

便可使用 RUNTIME_CLASS宏和CObject::IsKindOf函数以在运行时间决定对象类。如果DECLARE_DYNAMIC包含在类定义中,那么IMPLEMETN_DYNAMIC必须包含在类工具中。DECLARE_DYNCREATE

DECLARE_DYNCREATE(class_name)

说明:

使用DECLARE_DYNCRETE宏以便允许CObject派生类的对象在运行时刻自动建立。主机使用此功能自动建立新对象,例如,但它在串行化过程中从磁盘读一个对象时,文件及视图和框架窗应该支持动态建立,因为框架需要自动建立它。把DECLARE_DYNCREATE宏加入类的.H 文件中,然后在全部需要访问此类对象的.CPP文件中包含这一模式。如果

DECLARE_DYNCREATE包含在类定义中,那么 IMPLEMENT_DYNCREATE必须包含在类工具中。DECLARE_MESSAGE_MAP

DECLARE_MESSAGE_MAP()

说明:

用户程序中的每个CCmdTarget派生类必须提供消息映射以处理消息。在类定义的末尾使用DECLARE_MESSAGE_MAP宏。接着,在定义类成员函数的.CPP文件中,使用

BEGIN_MESSAGE_MAP宏,每个用户消息处理函数的宏项下面的列表以及

END_MESSAGE_MAP宏。

注释:

如果在DECLARE_MESSAGE_MAP之后定义任何一个成员,那么必须为他们指定一个新存取类

型(公共的,私有的,保护的)。

DECLARE_SERIAL

DECLARE_SERIAL(class_name)

说明:

DECLARE_SERIAL 为一个可以串行化的CObject派生类产生必要的C++标题代码。串行化是把某个对象的内容从一个文件读出和写入一文件。在.H文件中使用 DECLARE_SERIAL宏,接着在需要访问此类对象的全部.CPP文件中包含此文件。如果DECLARE_SERIAL包含在类定义中,那么 IMPLEMENT_SERIAL必须包含在类工具中。DECLARE_SERIAL宏包含全部

DECLARE_DYNAMIC,IMPLEMENT_DYCREATE的功能。

END_CATCH

END_CATCH

说明:

标识最后的CATCH或AND_CATCH块的末尾。

END_MESSAGE_MAP

END_MESSAGE_MAP

说明:

使用END_MESSAGE_MAP宏结束用户的消息映射定义

IMPLEMENT_DYNAMIC

IMPLEMENT_DYNAMIC(class_name,base_class_name)

说明:

通过运行时在串行结构中为动态CObject派生类访问类名和位置来产生必要的C++代码。在.CPP文件中使用IMPLEMENT_DYNAMIC宏,接着一次链接结果对象代码

IMPLEMENT_DYNCREATE

IMPLEMENT_DYNCREATE(class_name,base_class_name)

说明:

通过DECLARE_DYNCREATE宏来使用IMPLEMENT_DYNCREATE宏,以允许CObject派生类对象在运行时自动建立。主机使用此功能自动建立对象,例如,但它在串行化过程中从磁盘读去一个对象时,他在类工具里加入IMPLEMENT_DYNCREATE宏。若用户使用

DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏,那么接着使用RUNTIME_CLASS宏和

CObject::IsKindOf成员函数以在运行时确定对象类。若declare_dyncreate包含在定义中,那么

IMPLEMENT_DYNCREATE必须包含在类工具中。

IMPLEMENT_SERIAL

IMPLEMENT_SERIAL(class_name,base_class_name,wSchema)

说明:

通过运行时在串行结构中动态CObject派生类访问类名和位置来建立必要的C++代码。在.CPP 文件中使用IMPLEMENT_SERIAL宏,然后一次链接结果对象代码。

ON_COMMAND

ON_COMMAND(id,memberFxn)

说明:

此宏通过ClassWizard或手工插入一个消息映射。它表明那个函数将从一个命令用户接口(例

如一个菜单项或toolbar按钮)处理一个命令消息。当一个命令对象通过指定的ID接受到一

个Windows WM_COMMAND消息时,ON_COMMAND将调用成员函数memberFxn处理此消息。

在用户的消息映射中,对于每个菜单或加速器命令(必须被映射到一个消息处理函数)应该确实有一个ON_COMMAND宏语句。

ON_CONTROL

ON_CONTROL(wNotifyCode,id,memberFxn)

说明:

表明哪个函数将处理一个常规控制表示消息。控制标识消息是那些从一个控制夫发送到母窗口的消息。

ON_MESSAGE

ON_MESSAGE(message,memberFxn)

说明:

指明哪个函数将处理一用户定义消息。用户定义消息通常定义在WM_USER到0x7FF范围内。用户定义消息是那些不是标准Windows WM_MESSAGE消息的任何消息。在用户的消息映射中,每个必须被映射到一个消息处理函数。用户定义消息应该有一个ON_MESSAGE宏语句。ON_REGISTERED_MESSAGE

ON_REGISTERED_MESSAGE(nmessageVarible,memberFxn)

说明:

Windows的RegisterWindowsMesage函数用于定义一个新窗口消息,此消息保证在整个系统中

是唯一的。此宏表明哪个函数处理已注册消息。变量nMessageViable应以NEAR修饰符来定义。

ON_UPDATE_COMMAND_UI

ON_UPDATE_COMMAND_UI(id,memberFxn)

说明:

此宏通常通过ClassWizard被插入一个消息映射,以指明哪个函数将处理一个用户接口个更改

命令消息。在用户的消息映射中,每个用户接口更改命令(比讯被映射到一个消息处理函数)应该有一个ON_UPDATE_COMMAND_UI宏语句。

ON_VBXEVENT

ON_VBXEVENT(wNotifyCode,memberFxn)

说明:

此宏通常通过ClassWizard被插入一个消息映射,以指明哪个函数将处理一个来自VBX控制的

消息。在用户的消息映射中每个被映射到一消息处理函数的VBX控制消息应该有一个宏语句。RUNTIME_CLASS

RUNTIME_CLASS(class_name)

说明:

使用此宏从c++类民众获取运行时类结构。RUNTIME_CLASS为由class_name指定的类返回一个指针到CRuntimeClass结构。只有以DECLARE_DYNAMIC,DECLARE_DYNCREATE或DECLARE_SERIAL定义的CObject派生类才返回到一个 CRuntimeClass结构的指针。THROW

THROW(exception_object_pointer)

说明:

派出指定的异常。THROW中断程序的运行,把控制传递给用户程序中的相关的CATCH块。如果用户没有提供CATCH块,那么控制被传递到一个MFC模块,他打印出一个错误并终止运行。THROW_LAST

THROW_LAST()

说明:

此宏允许用户派出一个局部建立的异常。如果用户试图排除一个刚发现的异常,那么一般此异常将溢出并被删除。使用THROW_LAST,此异常被直接传送到下一个CATCH处理程序。TRACE

TRACE(exp)

说明:

把一个格式化字符串送到转储设备,例如,文件或调试监视器,而提供与printf相似的功能。

同MS_DOS下C程序的printf一样,TRACE宏是一个在程序运行时跟踪变量值的方便形式。在

DEBUG环境中,TRACE宏输出到afxDump。在Release版中他不做任何工作。

注释:

此宏只在MFC的DEBUG版中有效。

TRACE0

TRACE0(exp)

说明:

与 TRACE相似,但他把跟踪字符串放在代码段中,而不是DGROUP,因此使用少的DGROUP空间。TRACE0是一组跟踪宏的一个变体,这些宏可用于调试输出。这一组包括

TRACE0,TRACE1,TRACE2和TRACE3,这些宏不同在于所取参数的数目不同。TRACE0只取一个格式化字符串并可用于简单文本消息。TRACE1取一格式化字符串加上一个变量——一个将转储的变量。同样,TRACE2,TRACE3分别取2个或3个参数(在格式化字符串之后)。如果用户

以便以了应用程序的发行版,那么它只把数据转储到afxDump。

注释:

此宏只在MFC的DEBUG中有效。

TRACE1

TRACE1(exp,param1)

说明:

参见TRACE0

TRACE2

TRACE2(exp,param1,param2)

说明:

参见TRACE0

TRACE3

TRACE3(exp,param1,param2,param3)

说明:

TRY

TRY

说明:

使用此宏建立一TRY块。一个TRY识别一个可排除异常的代码块。这些异常在随后的CATCH 和

AND_CATCH块处理。传递是允许的:异常可以传递一个外部TRY块,或者忽略它们或者使用THROW_LAST宏。

VERIFY

VERIFY(booleanExpression)

说明:

在 MFC的DEBUG版中,VERIFY宏计算它的变量值。如果结果为0,那么宏打印一个诊断消息并中止程序。如果条件不为0,那么什么工作也不作。诊断有如下形式: assertion failed in file in line 其中name是源文件的名字,num是在源文件中失败的中止行号。在MFC的Release

版中,VERIFY计算表达式值但不打印或中止程序。例如:如果表达式是个函数调用,那么调用成功

侯捷stl源码剖析注释之42 sgi-stl-slist

完整列表 The Annotated STL Sources 1 G++ 2.91.57,cygnus\cygwin-b20\include\g++\stl_slist.h 完整列表 /* * Copyright (c) 1997 * Silicon Graphics Computer Systems, Inc. * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Silicon Graphics makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * */ /* NOTE: This is an internal header file, included by other STL headers. * You should not attempt to use it directly. */ #ifndef __SGI_STL_INTERNAL_SLIST_H #define __SGI_STL_INTERNAL_SLIST_H __STL_BEGIN_NAMESPACE #if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) #pragma set woff 1174 #endif // 單向串列的節點基本結構 struct __slist_node_base { __slist_node_base* next ; }; // 全域函式:已知某一節點,安插新節點於其後。 inline __slist_node_base* __slist_make_link (__slist_node_base* prev_node, __slist_node_base* new_node) { // 令 new 節點的下一節點為prev 節點的下一節點 new_node->next = prev_node->next; prev_node->next = new_node; // 令 prev 節點的下一節點指向new 節點 return new_node; } // 全域函式:找出某一節點的前一個節點。 inline __slist_node_base* __slist_previous (__slist_node_base* head, const __slist_node_base* node) {

STL源码剖析总结_第二章-空间配置器

2.空间配置器 2.1具备次配置力(sub-allocation)的SGI空间配置器 SGI含有两个空间配置器类,std::allocator内存分配类符合标准,但是仅仅是对operator new和operator delete简单封装一下而已;其次是SGI特殊的内存分配器std::alloc,其中实现采用了内存池,对于分配大量小容量的对象,可以大大减少内存碎片。 SGI标准的空间配置器std::allocator 这是对应的模板内联内存分配函数。实现起来也很简单,注意这里分配的内存仅仅是一块没有使用的空间而已,在上面并没有构造对象,后面讲解如何在上面构造对象。 模板内联内存释放函数,直接调用全局的operator delete释放对应的内存。

SGI特殊的空间配置器Std::alloc class Foo{…} Foo* pf = new Foo;//配置内存,然后构造对象delete pf;//将对象析构,然后释放内存 new的算是包含两个阶段:

1)调用::operator new 配置内存 2)调用Foo::Foo()构造对象内容 Delete算式也包含两个阶段 1)调用Foo::~Foo()将对象析构 2)调用::operator delete释放内存 为了精密分工,STL allocator将两个阶段的操作分开来,内存配置操作由alloc::allocate()负责,内存释放操作由alloc::deallocate()负责;对象构造由::construct()负责,对象析构由::destroy()负责。

2.stl_alloc.h 内存空间的分配和释放 内部使用malloc在堆中申请内存,其中制造了一个内存池,可以减少小型区块过多而造成的内存碎片问题。 SGI设计了双层级配置器,第一级配置器直接使用malloc()和free(),第二级配置器则视情况采用不同的策略:当配置区块超过128bytes时,采用第一级配置器,当配置区块小于128bytes时,采用第二级配置器,采用复杂的memory pool。它内存池实际上就是内部维护了16个自由链表,预先已经分配好了,当需要从内存池中取内存时候,直接从对应链表取出即可;当释放内存到内存池时候,直接将内存插入链表即可。每个链表中节点分别占用8、16、24、32、40、48、52、64、72、80、88、96、104、112、120、128字节。

MFC经典绘图方法总结

MFC经典绘图方法总结 Windows 绘图的实质就是利用windows提供的图形设备接口GDI(Graphics Device Interface)将图形会制在显示器上。 为了支持GDI绘图,MFC提供了两种重要的类: 设备上下文DC(Device Context)类,用于设置绘图属性和绘制图形; 绘图对象类,封装了各种GDI绘图对象,包括画笔、刷子、字体、位图、调色板和区域。 CDC类介绍: 在MFC中,CDC是设备上下文类的基类,派生类包括:CClientDC, CPaintDC, CWindowDC, CMetaFileDC类 CClientDC 客户区设备上下文,绘制客户区时 CPaintDC 一般发生在窗口需要重绘时 CWindwDC 可以绘制整个窗口,通常在窗口WM_NCPAINT消息的响应函数CWnd::OnNCPaint()中使用 CMetaFileDC 专门用于图元文件的绘制,图元文件记录一组GDI命令,重建图形输出。 CDC包含m_hDC和m_hAttribDC二个设备上下文。CDC指导所有对m_hDC的输出GDI调用(SetTextColor)以及对m_hAttribDC的大部分属性GDI调用(GetTextColor)。 CDC对象的重要函数如下: 1、为指定设备创建上下文virtual BOOL CreateDC(...) 比如创建一个为屏幕的设备上下文 CDC dc; dc.CreateDC("DISPLAY", NULL, NULL,NULL); 2、创建内存设备上下文,与指定设备上下文兼容virtual BOOL CreateCompatibleDC( CDC * pDC) 3、删除CDC对象对应的Windows设备上下文, 通常不调用该函数而是使用析构程序virtual BOOL DeleteDC(); 4、将HDC句柄转化为设备上下文的句柄: CDC *pDC=CDC::FromHandle( hDC ) 5、选择GDI对象入选到设备上下文中,一共有五种形式: CPen * SelectObject( CPen * pPen) ; CBrush * SelectObject( CBrush * pBrush) ; virtual CFont * SelectObject( CFont * pFont) ; CBitmap * SelectObject( CBitmap * pBitmap) ; int SelectObject( CRgn * pRgn) ;

STL源码剖析总结_第八章配接器

8 配接器 8.1 配接器之概观与分类 1、设计模式中对配接器的定义如下:将一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的classes可以一起运作。 2、容器配接器(应用于容器) stack和queue是两个容器配接器,底层默认由deque构成。stack封住了所有的deque对外接口,只开放符合stack原则的几个函数;queue封住了所有的deque 对外接口,只开放符合queue原则的几个函数。 3、迭代器配接器(应用于迭代器) 3.1 insert iterators 可以把一般迭代器的复制操作转变为插入操作。 insert iterators包括back_insert_iterator(专门负责尾端插入),front_insert_iterator (专门负责头端插入)和insert_iterator(可以负责任何位置执行插入)。主要观念是,每个insert iterators内部都维护有一个容器;容器有自己的迭代器,当客户端对insert iterators做赋值操作时,就在insert iterators中转为对该容器的迭代器做插入操作,其他的迭代器功能则被关闭。 3.2 reverse iterators reverse iterators将迭代器的行进方向逆转,使原本应该前进的operator++变成了后退操作,原本后退的operator—操作变成了前进操作。当迭代器被逆转,虽然实体位置不变,但逻辑位置必须改变,主要是为了配合迭代器区间的“前闭后开“习惯。

3.3 IOStream iterators IOStream iterators可以将迭代器绑定到某个iostream对象身上。绑定一个istream object(例如:std::cin),称为istream_iterator,拥有输入功能。绑定到ostream object (例如:std::cout),称为ostream_iteratpr,拥有输出功能。内部维护一个istream member,客户端对这个迭代器做的operator++操作,会被导引调用内部所含的那个istream member的输入操作。绑定一个ostream object,就是在ostream iterator内部维护一个ostream member,客户端对这个迭代器做的operator=操作,会被导引调用内部所含的那个ostream member的输出操作。 3.4 运用实例

MFC程序结构分析

MFC程序结构分析2008-11-08 16:39 分类:VC\MFC 字号:大中小 1 Windows程序工作原理 WINDOWS 程序设计是一种完全不同于传统的DOS 方式的程序设计方法,它是一种事件驱动方式的程序设计模式。在程序提供给用户的界面中有许多可操作的可视对象。用户从所有可能的操作中任意选择,被选择的操作会产生某些特定的事件,这些事件发生后的结果是向程序中的某些对象发出消息,然后这些对象调用相应的消息处理函数来完成特定的操作。WINDOWS 应用程序最大的特点就是程序没有固定的流程,而只是针对某个事件的处理有特定的子流程,WINDOWS 应用程序就是由许多这样的子流程构成的。 从上面的讨论中可以看出,WINDOWS 应用程序在本质上是面向对象的。程序提供给用户界面的可视对象在程序的内部一般也是一个对象,用户对可视对象的操作通过事件驱动模式触发相应对象的可用方法。程序的运行过程就是用户的外部操作不断产生事件,这些事件又被相应的对象处理的过程。下面是WINDOWS 程序工作原理的示意图。 2 建立应用程序 在介绍AppWizard 的时候,我们已经建立了一个名字为TEST 的工程,事实上这个框

架程序已经可以编译运行了。在BUILD 菜单中选择REBUILD ALL 菜单项,系统开始编译由 APPWIZARD 自动生成的程序框架中所有文件中的源代码,并且链接生成可执行的应用程 序。在BUILD 菜单中选择EXECUTE 菜单项,应用程序就开始开始运行了,虽然我们没有编 写一行代码,但是可以看出由系统自动生成的应用程序的界面已经有了一个标准WINDOWS 应用程序所需的几个组成部分,我们要做的事情是往这个应用程序添加必要的代码以完成 我们所需要的功能。 接下来将要对WINDOWS 自动生成的这个应用程序框架作详细的介绍,让你对MFC 方式的 WINDOWS 应用程序的工作原理有全面的认识,只有这样你才会知道应该如何往程序框架当 中添加需要的代码。 3 程序结构剖析 为了让您对MFC 方式的程序的框架有一个总体的认识,这里设计了一个表示程序中的 主要类之间的关系的图表:

MFC程序结构分析

主题: MFC深入浅出 1.MFC深入浅出 MFC概述 2. 3. 1.MFC是一个编程框架 2. 3.MFC (Microsoft Foundation Class Library) 中的各种类结 合起来构成了一个应用程序框架,它的目的就是让程序员在此基础 上来建立Windows下的应用程序,这是一种相对SDK来说更为简单 的方法。因为总体上,MFC框架定义了应用程序的轮廓,并提供了 用户接口的标准实现方法,程序员所要做的就是通过预定义的接口 把具体应用程序特有的东西填入这个轮廓。Microsoft Visual C++ 提供了相应的工具来完成这个工作:AppWizard可以用来生成初步 的框架文件(代码和资源等);资源编辑器用于帮助直观地设计用 户接口;ClassWizard用来协助添加代码到框架文件;最后,编译, 则通过类库实现了应用程序特定的逻辑。 4. 5. 6. 7. 1.封装 2. 3.构成MFC框架的是MFC类库。MFC类库是C++类库。这 些类或者封装了Win32应用程序编程接口,或者封装了应用 程序的概念,或者封装了OLE特性,或者封装了ODBC和DAO 数据访问的功能,等等,分述如下。 4. 5.(1)对Win32应用程序编程接口的封装 6. 7.用一个C++ Object来包装一个Windows Object。例 如:class CWnd是一个C++ window object,它把Windows window(HWND)和Windows window有关的API函数封装在C++ window object的成员函数内,后者的成员变量m_hWnd就 是前者的窗口句柄。 8. 9.(2)对应用程序概念的封装

如何成为一个程序员

如何成为一个程序员:想成为一个游戏程序员需要有以下资料 疯狂代码 https://www.360docs.net/doc/6e18198614.html,/ ?: http:/https://www.360docs.net/doc/6e18198614.html,/GameDevelopment/Article36086.html 、书籍: 算法和数据结构: 数据结构(C语言版)——严蔚敏、吴伟民 清华出版社 我觉得其配套习题集甚至比原书更有价值每个较难题都值得做下 Introduction to Algorithms第 2版 中文名算法导论 有关算法标准学习教材和工程参考手册在去年CSDN网站WebSite上其翻译版竟然评为年度 2十大技术畅销书同时员杂志上开设了“算法擂台”栏目这些溯源固本举动不由得使人对中国现今浮躁不堪所谓“IT”业又产生了线希望这本厚厚书幸亏打折我才买得起虽然厚达千页但其英文通俗晓畅内容深入浅出可见经典的作往往比般水准书还耐读还能找到MIT视频教程第节课那个老教授嘻皮笑脸后面就是长发助教上课了 C语言名题精选百则 窍门技巧篇——冼镜光 机械工业出版社 作者花费年时间搜集了各种常见C段极具窍门技巧性编程法其内容都是大有来头而且给出了详细参考资料如个普通Fibonacci数就给出了非递归解、快速算法、扩充算法等步步深入直至几无油水可榨对于视速度如生命连个普通浮点数转化为整数都另辟蹊径以减少CPU cycle游戏员怎可不看? 计算机算法基础(第 2版)—— 佘祥宣等 华中科大出版社 我看到几个学校研究生拿它作教材(研究生才开算法太开玩笑了吧)这本书薄是薄了点用作者话来说倒也“精辟

”其实此书是Fundamentals of Computer Algorithms缩写版不过原书出版太久了反正我是没找到 The Art of Computer ProgrammingVolume 1-3 作者Donald E. Knuth是我心目中和冯.诺依曼、Dijkstra、Shannon并列 4位大师这本书作者从读大学本科时开始写直写到博士时十年磨剑足见其下足了功夫可作为计算机技术核心——算法和数据结构终极参考手册创新处也颇多譬如常见Shell排序他在书中提出可用(3i-1)/2间隔这使其稍快于O(n1. 5)当然这套书描述高度数学化为此恐怕般人(我?)最好还得先看本数学预备书Concrete Mathematics(直译为混凝土数学?^-^)再说可惜是这套书才出到第 3卷并没有覆盖全部常见算法内容不过好在对于游戏员来说越常见算法用得越多这也不算是什么要命损失 STL源码剖析—— 侯捷 华中科大出版社 侯捷不用介绍了华人技术作家中旗舰说其有世界级水准也不为过这本书我以为是C和数据结构葵花宝典(欲练此功必先自宫)也就是说不下几层地狱很难看懂它要求预备知识太多了如STL、数据结构、泛型编程、内存管理都要很扎实(为此是不是还要看看有内存管理设计模式的称Small Memory Software这本书呢?)但是旦看懂真会是所向披靡 Data Structures for Game Programmers 每个数据结构例程都是个小游戏还用SDL库实现了个算法演示系统虽然内容失的于浅但起码让人了解了数据结构在游戏中作用 其实游戏并不比其它特殊甚至要求基本功更加扎实所以花时间做些看似和实际应用不甚相干习题对今后工作是大有裨益而且有些应用很广算法如常被人津津乐道[Page]A*算法及其变种牵涉到图检索周游和分枝-限界法恐怕还得读些艰深论文才能充分明白运用如Donald E. KnuthAn analysis of alpha-beta cutoffs其实还有不少此类

深入分析STL标准模板·vector

深入分析STL标准模板——vector ——计算机学院--马晶义 摘要:通过阅读c++的STL标准模板库中vector的源代码,分析STL 中的内部成员、接口、内存管理、封装等方面。Vector 是一种顺序性的容器,按照严格线性存储各种对象。它其实就是一种动态的数组,正如数组,vector有他们存储在存储单元相邻元素,这就意味着他们的元素可以被存取不只有使用迭代器还定期使用指针抵消元素。但是不像普通的数组,存储在向量自动处理,允许它的扩展和简约的需要。 关键字:STL、Vcector、内部成员函数、接口函数、封装 一、vector的声明及内部常用函数 1.vector的声明 vector c; 创建一个空的vector vector c1(c2); 创建一个vector c1,并用c2去初始化c1 vector c(n) ; 创建一个含有n个ElemType类型数据的vector; vector c(n,elem); 创建一个含有n个ElemType类型数据的vector,并全部初始化为elem; c.~vector(); 销毁所有数据,释放资源; 2.vector容器中常用的函数。(c为一个容器对象) c.push_back(elem); 在容器最后位置添加一个元素elem

c.pop_back(); 删除容器最后位置处的元素 c.at(index); 返回指定index位置处的元素 c.begin(); 返回指向容器最开始位置数据的指针 c.end(); 返回指向容器最后一个数据单元的指针+1 c.front(); 返回容器最开始单元数据的引用 c.back(); 返回容器最后一个数据的引用 c.max_size(); 返回容器的最大容量 c.size(); 返回当前容器中实际存放元素的个数 c.capacity(); 同c.size() c.resize(); 重新设置vector的容量 c.reserve(); 同c.resize() c.erase(p); 删除指针p指向位置的数据,返回下指向下一个数据位置的指针(迭代器) c.erase(begin,end) 删除begin,end区间的数据,返回指向下一个数 据位置的指针(迭代器) c.clear(); 清除所有数据 c.rbegin(); 将vector反转后的开始指针返回(其实就是原来的end-1) c.rend(); 将vector反转后的结束指针返回(其实就是原来的begin-1) c.empty(); 判断容器是否为空,若为空返回true,否则返回false

MFC原理结构说明

JEI
N.
MFC 原理結構說明
捷胤工業有限公司 總合研究所 (JISC)
CO .
LT D
捷胤工業有限公司 NEW JEIN Corp. Corp.

JEI
? 質流控制器(Mass?Flow?Controller)簡稱 MFC。簡 易的詮釋 MFC 是一種流量與控制閥組的精密組機 電組合,其測量方式與一般流量計最大的差別, 是在於它是利用熱感溫差 非接觸方式測量氣體 是在於它是利用熱感溫差、非接觸方式測量氣體 之質量流速。
N.
MFC 簡介
? MFC 應用於I C 工業、真空工業、食品工業、化 學工業以及醫療儀器等行業,需要求高精確度、 高穩定度及高品質等氣體流量控制,如:蝕刻、 物理 化學氣相沉積 高穩定度來達成 其應用 物理、化學氣相沉積、高穩定度來達成,其應用 之層面乃遍佈高科技產業。
CO .
LT D
捷胤工業有限公司 NEW JEIN Corp. Corp.

JEI
Intel
Connector
N.
MFC 主要結構
Mass flow sensor
CO .
Bypass
Zero switch
LT D
Control valve Outlet Block
PCB
捷胤工業有限公司 NEW JEIN Corp. Corp.

JEI
? 製程氣體中許多是有腐蝕性或容易液
化的氣體,其與氣體接觸的部份均是 化的氣體 其與氣體接觸的部份均是 以抗腐蝕性強的 SUS‐316L 做為材質, 整顆鑄造而成。
N.
MFC 本體 (Block)
CO .
LT D
捷胤工業有限公司 NEW JEIN Corp. Corp.

out......C语言

C++程序设计之四书五经(下篇) 荣耀/文我在上篇中“盘点”了TCPL和D&E以及入门教程、高效和健壮编程、模板和泛型编程等方面共十几本C++好书。冬去春来,让我们继续C++书籍精彩之旅。 标准库 当我还在研究院工作时,与同院另外两家研究所合作开发过一个大型水利枢纽调度集成项目。我们三家软件系统之间都要相互通信。在调试通讯模块时,细心的客户(一名好学的系统管理员)发现对于同一通信规约的解释代码,我的不超过30行,而对方的则超过了150行且很难看懂。这位系统管理员很纳闷,我说大家编程风格和习惯不一样,我使用了标准库,而他使用了传统C编程风格以及他所习惯的另外一些技术。 别误会!我绝无贬低这位合作伙伴的意思。事实上,我对那些真正有着深厚的C编程功力的程序员常常怀有钦佩之心。毕竟,C++能有今天的成功在很大程度上缘于它深深地植根于C。作为一名C++程序员,倘若不熟悉C++中的C,我往往会认为他的基本功是不扎实的,他的技术底气是不足的。 不过话又说回来,C++是一种多范型(paradigm)编程语言,具体采用哪种编程风格,专业程序员应该知道视具体情况而定。作为一名经常需要在现场做即兴开发的项目负责人,为了短平快地解决当务之急,我习惯尽量采用现有的库(和组件)。效率(以及强健性)久经验证的C++标准库已经摆在那儿了,何乐而不用呢? Nicolai M. Josuttis,《The C++ Standard Library: A Tutorial and Reference》原文版、中文版:《C++标准程序库:自修教程与参考手册》 这是一本百科全书式的C++标准库著作,是一本需要一再查阅的参考大全。它在完备性、细致性以及精确性方面都是无与伦比的。本书详细介绍了每一标准库组件的规格和用法,内

Disk-Based Container Objects

Disk-Based Container Objects Tom Nelson A container that's very large, or that must persist between programs, really needs to live on disk. C++ container class libraries have become a staple item in many programmers' toolkits. The introduction of templates has made these libraries notably robust and simple to use. However, their transient (memory-based) nature still imposes certain restrictions on their use. First, they cannot grow to arbitrary size if needed; second, they lack persistence, hence they disappear when the program shuts down. The second restriction is usually easier to cure than the first. To make a transient container "hold water," the container class could use a persistent streams facility to write the essential data members of each contained object to a disk file before shutting down. The next program invocation need only initialize another transient container, then sequentially extract each object from the persistent store and add it to the new container. In some cases, though, you can't guarantee that your run-time storage requirements won't overflow available memory. A priority or scheduling queue, for example, might need to process and store an unanticipated quantity of incoming data. You could write some objects to disk to free space in the container. However, to effectively process additions and deletions, a transient container normally requires the presence of all contained objects in memory at one time. When you write contained objects to a file, the logical structure of the container (pointers, state info, etc.) is lost. One solution consists of moving the entire container out to a disk file so that it becomes, in effect, a database, or "Containerized Database." This article will demonstrate techniques for building and using such disk-based container classes. They allow you to employ container objects of virtually any size regardless of memory constraints, which you can maintain indefinitely. Even when persistence is not necessary, the technique still permits arbitrary growth of the containers by storing overflow in temporary files, restricted only by available disk space. I will concentrate here on developing disk-based implementations for three fundamental structure types (lists, vectors, and binary trees). I discuss a few abstract types derived from them, and provide an example of a disk-based binary tree sort. Design and Performance Considerations

STL源码剖析总结_第五章关联式容器

5 关联式容器 标准的STL关联式容器分为set(集合)和map(映射表)两大类,以及这两大类的衍生体multiset(多键集合)和multimap(多键映射表),这些容器的底层机制均以RB-tree(红黑树)完成,红黑树也是一个独立的容器,但并不开放给外界使用。 SGI STL还提供了一个不在标准规格之列的关联式容器:hash table(散列表),以及hash table为底层机制的hash_set(散列集合),hash_map(散列映射表),hash_multiset(散列多键集合),hash_multimap(散列多键映射表)所谓关联式容器:每笔数据(每个元素)都一个键值(key)和一个实值(value)。当元素插入到关联式容器时,容器内部结构(可能是RB-tree ,也可能是hash table)便依照其键值大小,以某种特定规则将这个元素置于适当位置。关联式容器没有所谓的头尾。 关联式容器的内部结构是一个平衡二叉树(balanced binary tree),以便获得良好的搜寻效率,平衡二叉树包括AVL-tree,RB-tree,AA-tree。 5.1 树导览 1)二叉搜索树BST(binary search tree) 二叉搜索树的放置规则是:任何节点的键值一定大于其左子树中的每一个节点的键值,并小于其右子树的每一个节点的键值。因此从根节点一直往左走,直至无左路可走,即得最小元素;从根节点一直往右走,直至无右可走,即得最大元素。

2)平衡二叉搜索树(balanced binary search tree) 平衡:没有任何一个节点过深(深度过大) 3)AVL-tree(Adelson-Velskii-Landis tree) AVL tree是一个“加上了额外平衡条件”的二叉搜索树。其平衡条件的建立是为了确保整棵树的深度为O(logN),AVL tree要求任何节点的左右子树的高度相差做多为1,这是一个较弱的条件,但仍能保证“对数深度”平衡状态。

MFC视图结构框架分析

MFC文档视图结构框架分析 1:程序的“导火索”---theApp CmyApp theApp; 在声明对象的同时,调用其构造函数。按C++的语法,首先要调用其基类Cwinapp的构造函数. 这个文件主要用于应用程序的一些初始化操作。 class CWinApp : public CWinThread { DECLARE_DYNAMIC(CWinApp) public: // Constructor CWinApp(LPCTSTR lpszAppName = NULL); ………… } CWinApp::CWinApp(LPCTSTR lpszAppName) { if (lpszAppName != NULL) m_pszAppName = _tcsdup(lpszAppName); else m_pszAppName = NULL; // initialize CWinThread state AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE(); AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread; ASSERT(AfxGetThread() == NULL); pThreadState->m_pCurrentWinThread = this; ASSERT(AfxGetThread() == this); m_hThread = ::GetCurrentThread(); m_nThreadID = ::GetCurrentThreadId();

STL源码剖析总结_第三章-迭代器与traits编程技法

3.迭代器(iterators)概念与traits编程技法 STL的中心思想在于:将数据容器(containers)和算法(algorithms)分开,彼此独立设计,最后在以一贴胶着剂将它们撮合在一起。 3.1 迭代器设计思维——STL关键所在 STL中心思想:将数据容器和算法分开,彼此独立设计,最后再以已贴粘着剂将他们撮合在一起 3.2 迭代器是一种smart pointer 迭代器是一种行为类似指针的对象,指针的最常见最重要的是内容提领(取出指标所指物体的内容dereference)和成员访问(member access)。所以,迭代器最重要的编程工作是对operator *和operator->进行重载(overloading)工作。 auto_ptr 是一个用来包装原生指针的对象,内存漏洞问题可以借此解决。用法如下,和原生指针一模一样。 3.3 迭代器相应类别(associated types) 最常用的相应类别有五种 value type:迭代器所指对象的型别。 difference type :表示两个迭代器之间的距离,可用于表示容器的最大容量。pointer type:代表迭代器所指对象的指针类型。简言之,operator->()的返回类型。reference type:代表迭代器所指对象的引用类型。简言之,operator*()的返回类型。分为两种,不允许改变“所指对象之内容”者称为constant iterators,例如const int* pic,允许改变“所指对象之内容”者称为mutable iterators,例如int* pi。*pic/*pi的型别constant T&/T&就是reference type。 iterator category type:提出5种迭代器的类型标识。

如何学习数据结构

如何学习数据结构 1、数据结构学习一定要自己独立完成代码实现,虽然有时候你理 解内容了,但是实现上面还是会愈要很多困难的,解决这些困难会帮助你提高程序设计的能力的。 2、数据结构是计算机专业最重要最基础的一门课,对于有过编程 经验的人,结合自己的编程体会,去领悟它的思想;对于初学者,捡一种自己最熟悉的语言去分析它,总之千万不要陷在语言的细节上,要高屋建瓴的去领会数据结构的思想。而且随着编程经历的丰富对它的体会越深入,最初接触是对一些思想可能只是生硬的记忆,随着学习的深入逐渐领悟了很多。对于实在弄不懂的东东,就先记住!!! 3、将各种数据结构算法烂熟于胸,这是一个优秀程序员的必须具 备的基本素质,是后来进步的基石。书上的例子自己看看,然后不看书自己想想做成代码,在以后使用的时候看看能不能用这些数据结构来解决问题。 4、自己试着把书上的数据结构尽量写成可复用的独立模板(模 块),以后用着方便,学得也深刻,以后复习不用看书了,反复温习即便自己的代码就行了,说实话,找工作面试的时候数据结构几乎是必问的! 5、我觉的学数据结构,应该从算法入手,不能急,我现在还在搞 数据结构呀!不过现在觉的不那么难了呀!因为主要是算法,一点一点理清,会有柳暗花明的时候的。

6、数据结构要反复看书,量变引起质变,可能一开始看不太懂, 单当看多了的时候,你会茅塞顿开! 7、我觉得数据结构要的是思想,学的也是思想,但你至少要熟练 一门语言,要么怎能检验你的思想是否正确,强烈推荐《STL 源码剖析》!!!结合STL中的源码去分析,STL是我看到的最全 的以数据结构为宗旨的一种库,还建议你去下一个STLPORT,之 中的源码比VC提供的好些,很全,基本上能够用到的数据结构 都涉及到了,并且在学这个库的过程当中还可以学习一些设计 模式,还可以学习VC中的范型运算思想,等等,开始行动吧!!! 8、怎样学习数据结构,最好方法是一起讨论。 9、1)如果你没有学过C语言,或者C语言学的不好的时候把数据 结构当成一本数学书来学,它所讲述的都是一些简单的图论。 在你的大脑中的主线不能丢失:线性结构,树结构和图结构。 当你不再考虑复杂的程序设计时,仅仅研究个个离散点之间的 关系,似乎数据结构也就不会那么难了。 2)学习好了抽象的 离散点关系后,再巩固一下你的C语言水平,书中描述的都是 类C。因此你只要学习简单的C定义、判断、循环语句就基本 能看的懂课本中所有程序了。3)以上都完成后,从数据结构的 线性表开始。线性表中顺序表,似乎是为你学习C语言设计的,学好线性表的链表是你起步的关键。后面的树结构,图结构, 排序,查找都少不了链式结构,往往这个也是最难的。 4)看 程序的时候一定要自己在纸上画画,最好先学会画程序的流程

侯捷stl源码剖析注释47 sgi-stl-vector

完整列表 The Annotated STL Sources 1 G++ 2.91.57,cygnus\cygwin-b20\include\g++\stl_vector.h 完整列表 /* * * Copyright (c) 1994 * Hewlett-Packard Company * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Hewlett-Packard Company makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * * Copyright (c) 1996 * Silicon Graphics Computer Systems, Inc. * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Silicon Graphics makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. */ /* NOTE: This is an internal header file, included by other STL headers. * You should not attempt to use it directly. */ #ifndef __SGI_STL_INTERNAL_VECTOR_H #define __SGI_STL_INTERNAL_VECTOR_H __STL_BEGIN_NAMESPACE #if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) #pragma set woff 1174 #endif template // 預設使用 alloc 為配置器 class vector { public: // 以下標示 (1),(2),(3),(4),(5),代表 iterator_traits 所服務的5個型別。 typedef T value_type ; // (1) typedef value_type* pointer ; // (2) typedef const value_type* const_pointer ; typedef const value_type* const_iterator ;