一、引言

上一专题中,纯手动地完成了一个Windows应用程序,然而,在实际开发中,我们大多数都是使用已有的类库来开发Windows应用程序。MFC(Microsoft Foundation Class, 微软基础类库)是微软为了简化程序员的开发工作而将Windows API 封装到C++类中,利用这些类,程序员可以有效地完成Windows平台下应用程序的开发。本专题将详细剖析它。

二、利用向导创建一个MFC程序

用于帮助有效地开发Windows应用程序的类库除了MFC外,还有其他开源类库提供,比如说QT,只是QT不是微软开发的罢了,为了更好地剖析MFC,下面让我们用Visual Studio中的MFC模板和向导工具来创建一个基于MFC的单文档(SDI)应用程序。

  1. 启动Visual studio 2010,单击文件(FIle)菜单——>新建项目——>项目,在出现的项目窗口中选择Visual C++ 语言,然后选择MFC应用程序,并输入项目的名称为SDIMFC,具体如下图所示。

  

  2. 输入项目名称后点击确定按钮,将出现MFC应用程序向导窗口,点击下一步,应用程序类型选择:单个文档,如下图所示:

    

  3. 点击下一步,出现MFC向导的第三个对话框,复合文档支持保持默认选择,然后在出现的对话框中一直点击下一步来完成一个单文档MFC应用程序的创建。下面,按下Ctrl+F5来运行MFC应用程序,之后将看到我们创建的MFC应用程序界面,具体如下图所示:

在上面的程序中,我们并没有编写任何代码,运行它后就生成了一个带标题栏,系统菜单,具有最大化、最小化框和一个可调边框的应用程序,这一切的工作都是由MFC的向导工具帮我们完成,即该向导工具为我们生成了很多代码,下面就以这个简单的MFC程序来分析下MFC框架。

三、MFC框架详细解析

我们看下用MFC向导工具帮我们生成的哪些代码。你可以在VS中点击类视图选项卡(如果VS界面上没有看到类视图的,可以通过菜单栏视图—>类视图的方式显示出来),就可以看到如下图所示的类。

从上图可以发现,在MFC中,类的命名都是以字母“C”开头的,这种命名方式只是一种约定,让开发人员很快识别出该类是否属于MFC类库中的类。从图片可以看到,前面创建的单文档应用程序中有15个类,但这里我们只分析4个基本类,因为这4个基本类是每个Windows应用程序都会包含的,这4个类是:CMainFrame类、C+工程名(SDIMFC)+App类、C+工程名+Doc类(即CSDIMFCDoc类)和CSDIMFCView类(也是C+工程名+View的结构)。这4个类的基类都是MFC中类,基类的查看可以通过在VS类视图点击图标。关于MFC中类图层次结构图可以参考MSDN:http://msdn.microsoft.com/zh-cn/library/ws8s10w4.aspx,下图(摘自MSDN)很好地诠释了MFC中层次结构图类别。

3.1 MFC应用程序中的WinMain函数

前面对我们创建的MFC应用程序结构进行了一个简单的介绍,下面让我们深入剖析MFC应用程序的实现原理,在前一专题讲到,所有Window下窗口应用程序都要遵循这样一个过程:程序首先进入WinMain函数,然后设计窗口类、注册窗口类、创建窗口、显示和更新窗口、最后进入消息循环,将消息传递给窗口过程函数进行处理。然后在MFC应用程序中,我们使用VS的查找工具在MFC项目中查看WinMain函数却找不到,再查看CreateWindow函数也找不到,那么是不是MFC应用程序不需要WinMain函数,不需要创建窗口吗?这个疑问答案肯定是否定的,因为MFC应用程序一样是Windows应用程序,所以一定遵循上一专题介绍的过程,只是MFC提供的类帮我们对这些类进行了封装,这些函数都存在于MFC的源代码中,下面我们一起去找找程序的入口WinMain函数。

既然WinMain函数存在与MFC源码中,自然我们就要知道MFC源码在哪里了,在安装Visual studio的时候,我们已经安装了MFC的源代码,具体路径为:VS的安装路径\VC\atlmfc\src\mfc,如果你本机把VS安装到D:\Program Files(x86)的话,则MFC源代码路径在:D:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc。 下面利用Windows搜索工具查看WinMain函数的存在那个C++类中,在搜索之前,需要设置下Windows搜索工具,默认情况下,Windows搜索工具搜索内容在没有索引的位置,只搜索文件名的,这里需要设置为搜索文件名和内容,具体设置如下图所示(Win7下选择工具—>文件夹选项即可显示下图):

设置完成之后,在搜索框中输入WinMain,你将看到如下图所示的一个搜索结果:

WinMain函数的实现实际在appmodul.cpp文件里,用VS打开该cpp文件,你将看到WinMain函数的定义:

extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,_In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress: 4985)
{// call shared/exported WinMainreturn AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

上面代码中是_tWinMain函数啊,并不是我要的WinMain函数的,难道是找错了吗?对于这个疑问,答案也是否定的,我们没有找错,这里_tWinMain是一个宏定义,按F12即可以看到它代表的是WinMain。宏定义源码如下(存在于tchar.h头文件中):

/* Program */#define _tmain      main
// 宏定义
#define _tWinMain   WinMain

为了证明我们找到的WinMain正是我们需要找到的入口函数,我们可以在appmodul.cpp文件中_tWinMain函数中设置一个断点,然后按下F5按钮运行SDIMFC程序,我们发现,SDIMFC程序会在我们刚才设置的断点处停下来,具体如下图所示:

我们已经找到了WinMain函数在MFC中的实现了,但是并没有弄明白,我们创建的MFC程序是如何调用appmodul.cpp中的_tWinMain函数的,即程序中的MFC类如何与WinMain函数联系起来的呢?下面就让我们看看CSDIMFCApp类(至于为什么想到该类,因为其后缀为App,即应用程序,所以猜测程序在进入WinMain函数之前会先进入该类),在类视图中双击该类将在VS中看到该类的定义,从类定义可以知道,CSDIMFCApp类继承于CWinAppEx类,CWinAppEx类又继承于CWinApp,为了证明在WinMain函数之前先执行了CSDIMFCApp类中代码,我们在CSDIMFCApp类中的构造函数设置一个断点,然后按F5再运行下该程序,将发现程序首先停在CSDIMFCApp类的构造函数处,然后进入到_tWinMain函数(该断点是我们之前设置的断点)。这里又引起另外一个疑问了——为什么程序会首先调用CSDIMFCApp的构造函数呢?既然构造函数被调用了,肯定定义了该类的一个对象,然后,我们可以发现在CSDIMFCApp类中,定义了一个CSDIMFCApp类型的全局对象theApp,存在于SDIMFC.cpp文件中,具体定义代码如下:

// 唯一的一个 CSDIMFCApp 对象
CSDIMFCApp theApp; // 初始化对象,这种方式为调用类的无参数构造来初始化,所以会调用类的无参构造函数

然后我们在这个全局对象处设置一个断点,然后再按F5调试运行下该程序,你将发现程序执行的顺序为:theApp全局对象—>CSDIMFCApp构造函数(调用派生类的构造函数之前会调用其父类的构造函数)—>_tWinMain函数。在MFC程序中,theApp对象是用来唯一标识应用程序实例的,每个MFC程序有且仅有一个应用程序对象(这里为theApp对象)。

3.2 设计和注册窗口类

现在我们已经找到MFC中的WinMain函数了,根据前一专题的内容,接下来就是找到MFC应用程序中的窗口类和注册窗口类的代码,在上一专题中,窗口类和注册都是在WinMain函数中定义的,下面让我们看下MFC中WinMain函数都帮我们封装了什么,在MFC中的WinMain函数中只是简单对AfxWinMain函数进行调用,下面让我们看看AfxWinMain具体代码:

 1 // AfxWinMain函数
 2 int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 3     _In_ LPTSTR lpCmdLine, int nCmdShow)
 4 {
 5     ASSERT(hPrevInstance == NULL);
 6
 7     int nReturnCode = -1;
 8     CWinThread* pThread = AfxGetThread();
 9     CWinApp* pApp = AfxGetApp();
10
11     // AFX internal initialization
12     if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
13         goto InitFailure;
14
15     // App global initializations (rare)
16     if (pApp != NULL && !pApp->InitApplication())
17         goto InitFailure;
18
19     // Perform specific initializations
20     if (!pThread->InitInstance())
21     {
22         if (pThread->m_pMainWnd != NULL)
23         {
24             TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
25             pThread->m_pMainWnd->DestroyWindow();
26         }
27         nReturnCode = pThread->ExitInstance();
28         goto InitFailure;
29     }// PThread->Run函数是完成消息循环任务的
30     nReturnCode = pThread->Run();
31
32 InitFailure:
33 #ifdef _DEBUG
34     // Check for missing AfxLockTempMap calls
35     if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
36     {
37         TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
38             AfxGetModuleThreadState()->m_nTempMapLock);
39     }
40     AfxLockTempMaps();
41     AfxUnlockTempMaps(-1);
42 #endif
43
44     AfxWinTerm();
45     return nReturnCode;
46 }

上面代码首先调用AfxGetThread函数获得一个指向CWinThread类型的指针,然后再调用了AfxGetApp函数获得一个指向CWinApp类型的指针,再继续调用AfxWinInit函数进行AFX(以AFX为前缀的函数为应用程序框架函数,Application Framework)内部初始化,接着pApp调用InitApplication函数,该函数主要完成MFC内部管理方面的工作,该函数为虚函数,在CWinApp中的实现为(函数实现代码查找按照前面介绍的方式进行查看):

// 主要完成MFC内部管理工作
BOOL CWinApp::InitApplication()
{if (CDocManager::pStaticDocManager != NULL){if (m_pDocManager == NULL)m_pDocManager = CDocManager::pStaticDocManager;CDocManager::pStaticDocManager = NULL;}if (m_pDocManager != NULL)m_pDocManager->AddDocTemplate(NULL);elseCDocManager::bStaticInit = FALSE;LoadSysPolicies();return TRUE;
}

View Code

接着继续调用pThread的InitInstance函数,按F12可知,该函数声明为虚函数,根据类的多态性,这里AfxWinMain函数中调用的InitInstance函数为调用子类CSDIMFCApp的InitInstance函数,该函数的定义代码为:

 1 / CSDIMFCApp 初始化
 2 BOOL CSDIMFCApp::InitInstance()
 3 {
 4     // 如果一个运行在 Windows XP 上的应用程序清单指定要
 5     // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
 6     //则需要 InitCommonControlsEx()。否则,将无法创建窗口。
 7     INITCOMMONCONTROLSEX InitCtrls;
 8     InitCtrls.dwSize = sizeof(InitCtrls);
 9     // 将它设置为包括所有要在应用程序中使用的
10     // 公共控件类。
11     InitCtrls.dwICC = ICC_WIN95_CLASSES;
12     InitCommonControlsEx(&InitCtrls);
13
14     CWinAppEx::InitInstance();
15
16
17     // 初始化 OLE 库
18     if (!AfxOleInit())
19     {
20         AfxMessageBox(IDP_OLE_INIT_FAILED);
21         return FALSE;
22     }
23
24     AfxEnableControlContainer();
25
26     EnableTaskbarInteraction(FALSE);
27
28     // 使用 RichEdit 控件需要  AfxInitRichEdit2()
29     // AfxInitRichEdit2();
30
31     // 标准初始化
32     // 如果未使用这些功能并希望减小
33     // 最终可执行文件的大小,则应移除下列
34     // 不需要的特定初始化例程
35     // 更改用于存储设置的注册表项
36     // TODO: 应适当修改该字符串,
37     // 例如修改为公司或组织名
38     SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
39     LoadStdProfileSettings(4);  // 加载标准 INI 文件选项(包括 MRU)
40
41
42     InitContextMenuManager();
43
44     InitKeyboardManager();
45
46     InitTooltipManager();
47     CMFCToolTipInfo ttParams;
48     ttParams.m_bVislManagerTheme = TRUE;
49     theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
50         RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);
51
52     // 注册应用程序的文档模板。文档模板
53     // 将用作文档、框架窗口和视图之间的连接
54     CSingleDocTemplate* pDocTemplate;
55     pDocTemplate = new CSingleDocTemplate(
56         IDR_MAINFRAME,
57         RUNTIME_CLASS(CSDIMFCDoc),
58         RUNTIME_CLASS(CMainFrame),       // 主 SDI 框架窗口
59         RUNTIME_CLASS(CSDIMFCView));
60     if (!pDocTemplate)
61         return FALSE;
62     AddDocTemplate(pDocTemplate);
63
64
65     // 分析标准 shell 命令、DDE、打开文件操作的命令行
66     CCommandLineInfo cmdInfo;
67     ParseCommandLine(cmdInfo);
68
69
70
71     // 调度在命令行中指定的命令。如果
72     // 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
73     if (!ProcessShellCommand(cmdInfo))
74         return FALSE;
75
76     // 唯一的一个窗口已初始化,因此显示它并对其进行更新
77     m_pMainWnd->ShowWindow(SW_SHOW);
78     m_pMainWnd->UpdateWindow();
79     // 仅当具有后缀时才调用 DragAcceptFiles
80     //  在 SDI 应用程序中,这应在 ProcessShellCommand 之后发生
81     return TRUE;
82 }

在上面代码中,77行代码和78行代码为窗口的显示和更新,m_pMainWnd为我们创建的窗口,按F12转到其定义为指向CWnd类型的指针,Cwnd类是MFC为我们预定义的标准窗口类,现在我们已经找到MFC程序中的窗口类,接下来就是找到MFC中是如何注册窗口的,在MFC中,窗口类的注册是由AfxEndDeferRegisterClass函数完成的,其定义在Wincore.cpp文件中,AfxEndDeferRegisterClass函数内部又是通过AfxRegisterClass函数(该函数也定义在wincore.cpp文件中)来注册窗口类,由于篇幅的问题,这里就不贴其函数的定义源码,大家可以在本机中进行查看。

3.3 创建窗口

我们已经找到了MFC设计窗口和注册的封装,接下来就是MFC程序中是如何创建一个窗口的,该功能在MFC中是由CWnd类的CreateEx函数进行完成的,该函数的声明在afxwin.h文件中,具体代码如下:

virtual BOOL CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,int x, int y, int nWidth, int nHeight,HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam = NULL);

实现代码位于wincore.cpp文件中,我们程序中创建的是CMainFrame窗口,CMainFrame类继承于CFrameWndEx,该类又继承于CFrameWnd,CFrameWnd类的Create函数内部会调用CreateEx函数,而CFrameWnd的Create函数又由CFrameWnd类的LoadFrame函数调用。CFrameWnd类的Create函数声明位于afxwin.h文件中,其实现代码位于winfrm.cpp文件中,实现代码如下:

BOOL CFrameWnd::Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle,const RECT& rect,CWnd* pParentWnd,LPCTSTR lpszMenuName,DWORD dwExStyle,CCreateContext* pContext)
{HMENU hMenu = NULL;if (lpszMenuName != NULL){// load in a menu that will get destroyed when window gets destroyedHINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL){TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");PostNcDestroy();            // perhaps delete the C++ objectreturn FALSE;}}m_strTitle = lpszWindowName;    // save title for laterif (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext)){TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.\n");if (hMenu != NULL)DestroyMenu(hMenu);return FALSE;}return TRUE;
}

View Code

3.4 显示窗口和更新窗口

在CSDIMFCApp类的InitInstance函数内容即有窗口显示和更新窗口的代码,具体代码如下:

 // 唯一的一个窗口已初始化,因此显示它并对其进行更新m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();

3.5 消息循环

CWinThread类的Run函数就是完成消息循环这一任务的,该函数在AfxWinMain函数中进行了调用,其定义在thrdcore.cpp文件中,其定义代码如下所示:

// main running routine until thread exits
int CWinThread::Run()
{ASSERT_VALID(this);_AFX_THREAD_STATE* pState = AfxGetThreadState();// for tracking the idle time stateBOOL bIdle = TRUE;LONG lIdleCount = 0;// acquire and dispatch messages until a WM_QUIT message is received.for (;;){// phase1: check to see if we can do idle workwhile (bIdle &&!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE)){// call OnIdle while in bIdle stateif (!OnIdle(lIdleCount++))bIdle = FALSE; // assume "no idle" state
        }// phase2: pump messages while availabledo{// pump message, but quit on WM_QUITif (!PumpMessage())return ExitInstance();// reset "no idle" state after pumping "normal" message//if (IsIdleMessage(&m_msgCur))if (IsIdleMessage(&(pState->m_msgCur))){bIdle = TRUE;lIdleCount = 0;}} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));}
}

View Code

3.6 窗口过程函数

在AfxEndDeferRegisterClass函数中其中有一行这样的代码(下面代码红色标记处):

BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{// mask off all classes that are already registeredAFX_MODULE_STATE* pModuleState = AfxGetModuleState();fToRegister &= ~pModuleState->m_fRegisteredClasses;if (fToRegister == 0)return TRUE;LONG fRegisteredClasses = 0;// common initialization
    WNDCLASS wndcls;memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults// 设置窗口过程函数,这里指定时一个默认的窗口过程wndcls.lpfnWndProc = DefWindowProc;wndcls.hInstance = AfxGetInstanceHandle();wndcls.hCursor = afxData.hcurArrow;    .....}

但实际上,MFC中并不是把所有消息都交给DefWindowProc这一默认窗口过程进行处理的,而是采用了一种称为消息映射机制来处理各种消息,MFC消息映射机制指的是可以通过类向导为类添加消息处理函数,具体操作为,在类视图中右键某个类,然后选择类向导,在弹出的MFC类向导窗体中切换到消息选项卡来添加某个消息的处理函数,下图是CMainFrame执行类向导的截图:

该过程类似.NET中WinForm中通过某个控件的事件来添加事件处理函数。(WinForm中事件对应于MFC中的消息)。

至此,我们已经分析完了MFC程序的运行机制了,可以发现其经历过程和前一专题介绍是一致,只是MFC中我们不需要自己实现这些过程了,这些都由MFC框架帮我们封装好了,从而减少开发人员的任务量,将更多的时间放在实现程序的业务逻辑上面。下面让我们一起来梳理下MFC程序的运行过程:

  • 首先利用全局应用程序对象theApp启动应用程序。
  • 调用全局应用程序对象的构造函数,从而会调用基类CWinApp的构造函数,后者完成一些应用程序的初始化工作。
  • 进入到WinMain函数,即_tWinMain函数,在该函数中调用了AfxWinMain函数,后者获取子类(程序中指的CSDIMFCApp类)的指针,利用该指针调用InitInstance虚函数,根据多态原理,实际调用的是子类CSDIMFCApp的InitInstance函数,子类CSDIMFCApp的InitInstance函数完成应用程序的一些初始化工作,包括窗口类的注册、创建、窗口显示和更新。
  • 进入消息循环。虽然注册函数中设置了默认的窗口过程函数,但是,MFC应用程序实际上采用消息映射机制来处理各种消息的,当收到WM_QUIT消息时,将退出消息循环,程序结束。

四、文档/视图结构

我们创建的MFC程序除了主框架窗口外,还有一个窗口是视类窗口,对应于CView类,框架窗口是视类窗口的一个父窗口,它们之间的关系如下图所示。主框架窗口是整个应用程序外框所包括的部分,而视类窗口只是主框架窗口中的空白的地方。

在我们之前创建的MFC程序中还有一个CSDIMFCDoc类,它派生与CDocument类,后者的基类又是CCmdTarget,而CCmdTarget又派生于CObject类,从而,可以知道CSDIMFCDoc类不是一个窗口类,实际上它是一个文档类。MFC提供了一个文档/视图结构(Document/View),这里文档指的是CDocument类,而视图指的是CView类。微软在设计MFC时,考虑到数据本身应该与它的显示分离(这点在微软的很多技术中都有体现,例如Asp.net MVC ),于是就采用文档和视图结构来实现这一想法。数据的存储和加载由文档类来完成,数据的显示和修改由视图类来完成,从而把数据管理和显示方法分离开来

五、小结

到此,本专题的内容就介绍结束了,本专题主要剖析了MFC框架的运行机制,从而发现MFC应用程序同样遵循Win32 SDK程序相应的过程,包括设计窗口类、注册窗口类、创建窗口、显示和更新窗口、消息循环和窗口处理过程函数,只不过这些操作都被MFC本身封装好了。

转载于:https://www.cnblogs.com/zhili/p/MFCAnalyze.html

深入浅出话VC++(2)——MFC的本质相关推荐

  1. WPF学习之深入浅出话模板

    图形用户界面应用程序较之控制台界面应用程序最大的好处就是界面友好.数据显示直观.CUI程序中数据只能以文本的形式线性显示,GUI程序则允许数据以文本.列表.图形等多种形式立体显示. 用户体验在GUI程 ...

  2. 2020年如何在VC++的MFC中使用CDao*库

    2020年如何在VC++的MFC中使用CDao*库 摘要 : 详细介绍如何在VC++的MFC中使用CDao库,需要安装哪些库,实现对Access 1995的mdb文件调用.选择MFC作为静态库解决CD ...

  3. VC++之MFC从零开始实现windows系统任务管理器(一、windows任务管理器界面的制作)

    VC++之MFC从零开始实现windows系统任务管理器(一.windows任务管理器界面的制作) 通过本例程从零开始使用VC++与MFC实现一个基于windows的任务管理器. 最终效果图 软件工具 ...

  4. 友元 java_深入浅出话友元

    深入浅出话友元 小序: 有一阵子没来打扫Blog了--技术这东西,就是走走停停的,一段时间就会遇到一个瓶颈.迷茫一下,然后发现与其因为迷茫而停滞不前,不如瞄准一个大方向勇敢地游下去. 这两天有几个正在 ...

  5. VC++、MFC最好的开源项目

    介绍:介绍一下用VC++/MFC写的最好的开源项目. Sourceforge.net中有许多高质量的VC++开源项目,我列举了一些可以作为VC++程序员的参考. 正文: VC++.MFC中最好的开源项 ...

  6. 深入浅出话命令(Command)-笔记(-)

    深入浅出话命令(Command)-笔记(-) 一 基本概念 命令的基本元素: 命令(Command):实现了ICommand接口的类,平常使用最多的是RoutedCommand类. 命令源(Comma ...

  7. VC用MFC开发的圆形进度条控件

    DownLoad Src VC用MFC开发的圆形进度条控件 visualsan@yahoo.cn NUAA zss 在NBA2007游戏里,还有很多科幻电影里,经常可以看到圆形进度条.有的用来显示导弹 ...

  8. 深入浅出话多态(上)——具体而微

    深入浅出话多态(上)--具体而微 小序 前几天写了一篇<深入浅出话委托>,很多兄弟姐妹发Mail说还算凑合,又有兄弟说能不能写一篇类似的文章,讲解一下什么是"多态".一 ...

  9. C++、VC++、MFC网页自动注册、登陆、发帖、留言,QQ注册、QQ申请器源码、注册邮箱源码、自动发帖源码...

    C++.VC++.MFC网页自动注册.登陆.发帖.留言,QQ注册.QQ申请器源码.注册邮箱源码.自动发帖源码   参考资料: 自动登录yahoo邮箱http://blog.csdn.net/suisu ...

最新文章

  1. python怎么输出结果_如何在python中打印SQLite查询的结果?
  2. elasticsearch查询及logstash简介
  3. Word中以交叉引用的方式插入参考文献
  4. js内存泄漏常见的四种情况(From LeuisKen)
  5. 计算机网络的各层及其协议,计算机网络的体系结构 (architecture) 是计算机网络的各层及其协议的集合...
  6. jQuery 使用收集
  7. Stopwatch 类
  8. 计算理论笔记 10月10日
  9. 【扩频通信】基于matlab直接序列扩频通信【含Matlab源码 1004期】
  10. android从子页面跳转回主页面注意
  11. python面试专题--with 关键字与上下文管理
  12. 全国宏观经济指标(图表以及相应的大致注释)
  13. python写入文件到docx_从python中的txt文件写入docx文件
  14. Pathon安装教程环境配置教程(Pathon2.7和3.7的区别)
  15. linux下7z压缩包解压
  16. Python之3~20的乘积
  17. 关于Ubuntu18.04双系统安装后无法连接WiFi问题解决方法
  18. 存储系统测试工具学习之vdbench
  19. 计算1~50之间的素数之和
  20. 【研究生】Yann LeCun、吴恩达等的2019年AI趋势预测

热门文章

  1. 知荐 | 自动驾驶的“大脑”:决策规划篇
  2. PreparedStatement是如何防止SQL注入的?
  3. Kafka、 RabbitMQ、Redis、 ZeroMQ、 ActiveMQ、 Kafka/Jafka 对比
  4. linux之expect
  5. Java虚拟机笔记(五):JVM中对象的分代
  6. c++--类的作用域
  7. C# semaphore的使用
  8. 2015-2016-1学期 《信息安全系统设计基础》课程总结
  9. 最短路径 一 Dijkstra 模板(O(n^2))
  10. eclipse - 自动换行