




已阅读5页,还剩17页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C+ Plus: 使用 Visual C+ 2008 功能包加强 Windows 应用程序豆豆网 技术应用频道2009年02月09日 【字号:小 中 大】 收藏本演示 MFC 中的两个新用户界面功能:Office 功能区和选项卡式多文档界面 (MDI)。 本文讨论: Microsoft 基础类库更新 使用 C+ 对功能区进行编程 使用 C+ 实现选项卡式 MDI 多态函数和智能指针本文以 Visual C+ 功能包的预发布版为基础。文中包含的所有信息均有可能变更。 本文使用了以下技术:Visual Studio 2008,MFC 目录 Office 功能区用户界面选项卡式多文档界面标准 C+ 库中的新功能多态函数对象智能指针作为一名 Visual C+ 开发人员 ,您在最近几年可能有点被冷落的感觉,因为与 Visual C# 相比,似乎 Microsoft 向 Visual C+ 添加的新特性和新功能要少很多。事实上,尽管 Visual C+ 编译器在性能、安全性和标准符合性等方面始终在不断改进,但很长时间以来在新库和生产率功能方面却做的比较少。后来虽然更新了 MFC 以更好地支持 Windows Vista,但仍有许多工作需要完成。但是现在,为了对那些使用本机代码和 MFC 的开发人员提供更好的支持,Microsoft 发布了 Visual C+ 2008 功能包。以下是 Visual C+ 更新的一些主要内容。此功能包包括用于构建现代用户界面的一大组新 MFC 类。它还包括作为技术报告 1 (TR1) 的一部分添加到标准 C+ 库中的大量功能。TR1 是 C+ 委员会所采用的首个针对标准 C+ 库的主要更新和添加内容。多年以来,传统模式的单个和多个文档/视图应用程序、菜单、工具栏和对话框一直是 MFC 开发的一个主要方面。如果想让 MFC 应用程序看起来更现代一点,用户需要自己想办法。演示 MFC 中的两个新用户界面功能:Office 功能区和选项卡式多文档界面 (MDI)。 现在则完全不同了。MFC 现在包括了许多新的用户界面模式,其中甚至还有类似于 Microsoft Office 和 Visual Studio 中的可停靠窗格。它还完全支持 Microsoft Office 功能区用户界面以及众多其他新控件、对话框和窗口等。接下来,我将演示 MFC 中的两个新用户界面功能:Office 功能区和选项卡式多文档界面 (MDI)。Office 功能区用户界面到目前为止,我相信您已见过了新的 2007 Microsoft Office 系统功能区元素,并且您可能想知道如何在自己的应用程序中营造出这种效果。令人高兴的是,现在可以非常轻松地向 MFC 框架窗口添加功能区栏。许多新功能都依赖于新版本的 CwinApp、CFrameWnd 和 CMDIFrameWnd 类;这些类代表着大多数 MFC 应用程序的基础。CWinAppEx 由 CwinApp 派生而来,应该用作应用程序对象的基类。CFrameWndEx 由 CframeWnd 派生而来,应该用作单文档界面 (SDI) 框架窗口的基类。同样,CMDIFrameWndEx 由 CMDIFrameWnd 派生而来,应该用作 MDI 框架窗口的基类。这些新的基类提供了支持众多新用户界面功能(如可停靠、可调整大小的窗口窗格以及工作区持久性等)所需的全部要素。图 1 显示了可支持功能区栏的最小应用程序对象。如您所见,Application 类由 CwinAppEx 派生而来,可实现大家所熟悉的 InitInstance 成员函数(通常用于创建应用程序的主窗口)。千万不要忘记调用 SetRegistryKey 成员函数来设置应用程序设置的注册表位置,因为框架类要依赖于它。然后,InitInstance 继续以通常的方式创建主窗口。Figure1功能区应用程序对象 class Application : public CWinAppExpublic:virtual BOOL InitInstance();BOOL Application:InitInstance()SetRegistryKey(LSampleCompanySampleProduct);m_pMainWnd = new MainWindow;m_pMainWnd-ShowWindow(m_nCmdShow);m_pMainWnd-UpdateWindow();return TRUE; 图 2 中的代码显示了具有功能区栏和应用程序按钮的一个最小 SDI 框架窗口。应用程序按钮并不是必需的,但通常会与功能区栏结合使用,为应用程序提供各种各样的主菜单,以代替传统的“文件”菜单。Figure2功能区框架窗口 class MainWindow : public CFrameWndExDECLARE_MESSAGE_MAP()public:MainWindow();private:int OnCreate(CREATESTRUCT* createStruct);CMFCRibbonBar m_ribbon;CMFCRibbonApplicationButton m_appButton;BEGIN_MESSAGE_MAP(MainWindow, CFrameWndEx)ON_WM_CREATE()END_MESSAGE_MAP()MainWindow:MainWindow()Create(0, / class nameLMFC Ribbon Sample Application);int MainWindow:OnCreate(CREATESTRUCT* createStruct)if (-1 = _super:OnCreate(createStruct)return -1;if (-1 = m_ribbon.Create(this)return -1;m_appButton.SetImage(IDB_APP_BUTTON);m_ribbon.SetApplicationButton(&m_appButton,CSize(45, 45);CMFCRibbonMainPanel* appButtonMenu =m_ribbon.AddMainCategory(LMenu,IDB_APP_BUTTON_MENU_SMALL,IDB_APP_BUTTON_MENU_LARGE);appButtonMenu-Add(new CMFCRibbonButton(ID_FILE_NEW,L&New,0, / small image index0); / large image indexappButtonMenu-Add(new CMFCRibbonButton(ID_FILE_OPEN,L&Open.,1, / small image index1); / large image indexappButtonMenu-AddToBottom(new CMFCRibbonMainPanelButton(ID_APP_EXIT,LE&xit,15);/small image indexCMFCRibbonCategory* category = m_ribbon.AddCategory(LHome,IDB_RIBBON_CAT_HOME_SMALL,IDB_RIBBON_CAT_HOME_LARGE);CMFCRibbonPanel* panel = category-AddPanel(LClipboard);panel-Add(new CMFCRibbonButton(ID_EDIT_PASTE,LPaste,0, / small image index0); / large image indexpanel-Add(new CMFCRibbonButton(ID_EDIT_CUT, LCut, 1);panel-Add(new CMFCRibbonButton(ID_EDIT_COPY, LCopy, 2);panel-Add(new CMFCRibbonButton(ID_EDIT_SELECT_ALL,LSelect All, -1);m_ribbon.AddCategory(LInsert,IDB_RIBBON_CAT_HOME_SMALL,IDB_RIBBON_CAT_HOME_LARGE);CMFCVisualManager:SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerOffice2007);CMFCVisualManagerOffice2007:SetStyle(CMFCVisualManagerOffice2007:Office2007_LunaBlue);return 0; 从概念上讲,功能区由多个被称为类别的选项卡组成,每个选项卡都承载着一组面板。反过来,这些面板又承载着功能区元素或控件,它们分别代表特定于应用程序的各种操作。如果功能区承载着应用程序按钮(左上角的大圆按钮),则在用户单击应用程序按钮时所显示的弹出窗口中也会显示一个面板,它被视为功能区的主类别。CMFCRibbonBar 类可实现功能区栏本身,而 CMFCRibbonApplicationButton 类则代表功能区栏所承载且显示在窗口框架左上角的应用程序按钮。功能区栏通常是在 WM_CREATE 消息处理程序中创建和准备的。要创建功能区栏,只需调用 CMFCRibbonBar 的 Create 成员函数,以提供将其附加到其中的窗口框架的地址即可。然后根据需要填充它。AddMainCategory 成员函数将主类别添加到功能区,并返回一个指向 CMFCRibbonMainPanel(您可以向其中添加将要显示在此面板中的功能区元素)的指针。通过调用 AddCategory 成员函数,可向其中添加更多的类别来表示功能区的选项卡。AddCategory 返回一个指向 CMFCRibbonCategory 对象(您可使用其 AddPanel 成员函数向其中添加面板)的指针。AddPanel 返回一个指向 CMFCRibbonPanel 对象(可像使用功能区的主面板一样向其中添加功能区元素)的指针。最后,您可使用 CMFCVisualManager:SetDefaultManager 静态成员函数来设置负责处理框架窗口的样式和外观的可视化管理器。图 3 显示了功能区应用程序的可能外观(假定您已为功能区栏上的按钮添加了必要的事件处理程序)。图 3功能区示例应用程序选项卡式多文档界面MFC 一直都支持 MDI 实现及其文档/视图体系结构,但图 4 中所示的传统 MDI 早已过时,用户可能会认为您的应用程序从 Windows 95 开始就再也没有更新过。现在,绝大多数用户都期望可通过窗口边缘的选项卡来访问多个文档,而这正是新的 CMDIFrameWndEx MDI 框架窗口所提供的功能。图 4石器时代的窗口您需要更新多文档/视图应用程序对象以支持新的框架窗口。图 5 显示了满足需要的最小应用程序对象。它类似于传统的 MDI 应用程序对象,但有几点值得一提。Figure5选项卡式 MDI 应用程序对象 class Application : public CWinAppExDECLARE_MESSAGE_MAP()public:virtual BOOL InitInstance();BEGIN_MESSAGE_MAP(Application, CWinAppEx)ON_COMMAND(ID_FILE_NEW, &CWinAppEx:OnFileNew)END_MESSAGE_MAP()BOOL Application:InitInstance()SetRegistryKey(LSampleCompanySampleProduct);VERIFY(InitContextMenuManager();AddDocTemplate(new CMultiDocTemplate(IDR_CHILDFRAME, RUNTIME_CLASS(Document), RUNTIME_CLASS(CMDIChildWndEx), RUNTIME_CLASS(View);MainWindow* mainWindow = new MainWindow();VERIFY(mainWindow-LoadFrame(IDR_MAINFRAME);m_pMainWnd = mainWindow;m_pMainWnd-ShowWindow(m_nCmdShow);m_pMainWnd-UpdateWindow();return TRUE; 首先,子窗口框架的运行时类是 CMDIChildWndEx,而非传统的 CMDIChildWnd 类。要准备在选项卡式视图之间进行切换时使用的菜单管理器,还需调用 InitContextMenuManager 函数。图 6 显示了最小 MDI 框架窗口。同样,您会非常高兴地看到开启此功能是多么地简单。实际只需调用 EnableMDITabbedGroups 成员函数来启用 MDI 选项卡式分组功能即可。CMDITabInfo 类提供了各种成员变量,可使用它们来自定义选项卡式分组的外观和行为。顾名思义,它甚至还允许用户拖动不同的视图来创建垂直或水平对齐的选项卡组。图 7 显示了其可能的外观。Figure6选项卡式 MDI 框架窗口 class MainWindow : public CMDIFrameWndExDECLARE_DYNCREATE(MainWindow)DECLARE_MESSAGE_MAP()private:int OnCreate(CREATESTRUCT* createStruct);IMPLEMENT_DYNCREATE(MainWindow, CMDIFrameWndEx)BEGIN_MESSAGE_MAP(MainWindow, CMDIFrameWndEx)ON_WM_CREATE()END_MESSAGE_MAP()int MainWindow:OnCreate(CREATESTRUCT* createStruct)if (-1 = _super:OnCreate(createStruct)return -1;CMDITabInfo tabInfo;tabInfo.m_bAutoColor = true;tabInfo.m_bDocumentMenu = true;EnableMDITabbedGroups(true, tabInfo);return 0; 图 7现代选项卡式 MDI 应用程序标准 C+ 库中的新功能正如我所提到的,功能包还包括作为 TR1 的一部分添加到标准 C+ 库中的大量附加功能。其中包括支持引用计数的智能指针、多态函数包装、基于哈希表的容器、正则表达式等等。下面我将介绍其中的一些新 TR1 功能。多态函数对象在许多应用程序中都有一个至关重要的功能,就是能够将函数作为一个值加以引用并能够将其作为参数来传递或存储起来以备今后使用。此概念可用于实现各种常见的构造,包括回调函数、事件处理程序和异步编程功能等。但是,函数在 C+ 中非常难于处理。函数设计的驱动力主要源自与 C 的兼容性的要求以及对优良性能的要求。尽管实现了这些目标,但在将函数视为可存储、可传递并最终能够异步调用的对象方面,却并未能使其变得简单一些。让我们来看一看 C+ 中常见的一些类似函数的构造。首先,是一个不错的古老非成员函数:int Add(int x, int y)return x + y; 正常情况下,可通过如下方法调用它:int result = Add(4, 5);ASSERT(4 + 5 = result); 另一个常见的类似函数的构造是函数对象(即算符):class AddFunctorpublic:int operator()(int x, int y) constreturn x + y; 由于它实现调用运算符,因此可像使用函数一样来使用函数对象: AddFunctor fo; int result = fo(4, 5); ASSERT(4 + 5 = result); 接下来是非静态成员函数: class Adder public: int Add(int x, int y) const return x + y; ; 当然,调用成员函数需要使用对象:Adder adder;int result = adder.Add(4, 5);ASSERT(4 + 5 = result); 到目前为止一切顺利。现在,假设您需要将这些类似函数的构造存储起来以备今后使用。可按如下方式定义一个能存储指向非成员函数的指针的类型:typedef int (*FunctionPointerType)(int x, int y); 也可将函数指针作为函数来使用:FunctionPointerType fp = &Add;int result = fp(4, 5);ASSERT(4 + 5 = result); 尽管函数对象也可以存储下来,但它无法与函数指针一起以多态形式存储。成员函数可存储在 pointer-to-member-function 中:Adder adder;typedef int (Adder:*MemberFunctionPointerType)(int x, int y);MemberFunctionPointerType mfp = &Adder:Add; 但是,pointer-to-member-function 类型与 pointer-to-non-member-function 类型不兼容,因此无法与其非成员函数竞争者一起以多态形式存储。即使可以,成员函数仍需要一个对象来提供成员函数调用的上下文:int result = (adder.*mfp)(4, 5);ASSERT(4 + 5 = result); 我想我不必再做解释您也应该明白我的意思了。幸运的是,新的 tr1:function 类模板提供了一个解决方案。tr1:function 类模板为在其模板参数中定义的函数类型保存着一个可调用对象。接下来,我将使用非成员函数对其进行初始化:function f = &Add;int result = f(4, 5);ASSERT(4 + 5 = result); 使用函数对象来初始化也一样轻松:function f = AddFunctor(); 您甚至还可以使用新的函数绑定功能通过成员函数对其进行初始化:function f = bind(&Adder:Add, &adder, _1, _2); 有关 bind 函数的内容我会在稍后做介绍,但在这里您需要了解的就是现在可将单个函数包装绑定到非成员函数、函数对象甚至成员函数中。可将其存储下来并在今后随时调用,所有这一切都是以多态形式执行的。函数包装也是可以重新绑定的,并且可以像普通的函数指针一样设置为空值:function f;ASSERT(0 = f);f = &Add;ASSERT(0 != f);f = bind(&Adder:Add, &adder, _1, _2); bind 函数模板的功能要比标准 C+ 库中的函数对象适配器强大得多尤其是 std:bind1st() 和 std:bind2nd()。在这个示例中,bind 的第一个参数是成员函数的地址。第二个参数是对象的地址,届时将在此对象中调用成员。此示例中的最后两个参数定义了调用函数时将要解析的占位符。当然,bind 并不仅限于成员函数。您可通过绑定标准 C+ 库的 multiplies 函数对象来创建一个平方函数,利用此函数可生成一个能得出参数平方结果的单参数函数:function square = bind(multiplies(), _1, _1);int result = square(3);ASSERT(9 = result); 请注意,tr1:function 类模板非常适合与标准 C+ 库算法一起使用。给定一个整数容器,就可以使用成员函数生成所有值的总和,如下所示:function f = / initializeint result = accumulate(numbers.begin(),numbers.end(),0, / initial valuef); 请记住,tr1:function 类模板可能会禁止编译器优化(如内联),但如果您只是直接使用函数指针或函数对象则可能不会出现这一问题。因此,请仅在必要时才使用 tr1:function 类模板,例如当使用可能会被重复调用的累积算法时。如果可能,应直接将函数指针、成员函数指针(使用 TR1 的 mem_fn 改写过的)以及函数对象(如 bind 所返回的)传递给标准 C+ 库算法和其他模板化的算法。让我们接着往下看。接下来还有个更有趣的问题。假设有个 Surface 类代表一些绘图表面,还有个 Shape 类,它可以将其自身绘制到表面上:class Surface/.;class Shapepublic:void Draw(Surface& surface) const; 现在考虑一下怎样才能够将容器中的每个形状都绘制到给定表面上。您可能会考虑使用 for_each 算法,如下所示:Surface surface = / initializefor_each(shapes.begin(), shapes.end(), bind(&Shape:Draw, _1, surface); / wrong 在这里,我打算利用 bind 函数模板来针对形状容器的每个元素调用成员函数,从而将表面作为参数绑定到 Draw 成员函数。但遗憾的是,这要取决于 Surface 的定义方式,有时可能无法按预期的那样运行或编译。之所以出现这个问题,是因为当您实际需要的是一个引用时,bind 函数模板却试图生成表面副本。值得庆幸的是,TR1 还引入了 reference_wrapper 类模板,它允许您将引用视为一个可随意复制的值。由于类型推断功能的存在,ref 和 cref 函数模板可简化 reference_wrapper 对象的创建过程。借助于 reference_wrapper,for_each 算法现在可以简单有效地将形状成功绘制到表面上:for_each(shapes.begin(), shapes.end(), bind(&Shape:Draw, _1, ref(surface); 正如您所设想的,对于新的函数包装、绑定功能和引用包装,可以通过多种方式来组合它们,从而灵活地解决各种问题。智能指针智能指针对 C+ 开发人员而言是不可或缺的工具。我通常使用 ATL 的 CComPtr 来处理 COM 接口指针,使用标准 C+ 库的 auto_ptr 来处理原始 C+ 指针。后者非常适合需要动态创建 C+ 对象并能够确保当 auto_ptr 对象超出范围时可以将对象安全删除的情形。智能指针像 auto_ptr 一样都非常有用,但它只能安全地用在少数情形下。这主要是因为它所实现的所有权转移语义。即,如果复制或分配 auto_ptr 对象,则基础资源的所有权将被转移,原始 auto_ptr 对象将失去它。只有当您对资源分配拥有精细的控制权时它才会有明显作用,但很多情况下您可能需要共享对象,这时实现共享所有权语义的智能指针将会非常有用。更为重要的是,auto_ptr 无法与标准 C+ 库容器一起使用。TR1 引入了两个新的智能指针,它们协同工作来提供多种用途。shared_ptr 类模板的工作方式与 auto_ptr 十分相似,但它不能转移资源的所有权,它只是增加资源的引用计数。如果用来保存对象引用信息的最后一个 shared_ptr 对象被破坏或重置,资源将被自动删除。通过 weak_ptr 类模板与 shared_ptr 的协同工作,调用方可以在不影响引用计数的情况下引用资源。如果在对象模型中有循环关系或打算实现缓存服务,则这将非常有用。它还非常适合与标准 C+ 库容器一起使用!作为对比,请看一看以下的 auto_ptr 用法:auto_ptr ap(new int(123);ASSERT(0 != ap.get();/ transfer ownership from ap to ap2auto_ptr ap2(ap);ASSERT(0 != ap2.get();ASSERT(0 = ap.get(); auto_ptr 复制构造函数将所有权从 ap 传输到 ap2。shared_ptr 的行为同样是可预测的:shared_ptr sp(new int(123);ASSERT(0 != sp);/ increase reference count of shared objectshared_ptr sp2(sp);ASSERT(0 != sp2);ASSERT(0 != sp); 从内部来说,引用相同资源的所有 shared_ptr 对象都共享一个控制块,此控制块将跟踪共同拥有资源的 shared_ptr 对象的数量以及引用此资源的 weak_ptr 对象的数量。稍后我将展示如何使用 weak_ptr 类模板。与 auto_ptr 类似的成员函数由 shared_ptr 提供。其中包括解引用操作符和箭头操作符、用来替换资源的 reset 成员函数以及返回资源地址的 get 成员函数。此外还提供一些特有的成员函数(其中包括一个恰好也
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年工业互联网平台入侵检测系统实时监控与优化在智能制造中的应用报告
- 2025年基层医疗卫生机构信息化建设中的医疗信息化产业链协同发展策略报告
- 2025年新能源汽车充电基础设施投资策略:充电站投资效益与可持续发展报告001
- 教师招聘之《小学教师招聘》考前冲刺练习题带答案详解(达标题)
- 教师招聘之《小学教师招聘》高分题库及完整答案详解(名师系列)
- 教师招聘之《幼儿教师招聘》综合检测提分附参考答案详解【完整版】
- 教师招聘之《小学教师招聘》考前冲刺测试卷附参考答案详解【黄金题型】
- 2025年教师招聘之《小学教师招聘》试题标准卷附答案详解
- 教师招聘之《幼儿教师招聘》考前冲刺模拟题库提供答案解析附答案详解(精练)
- 教师招聘之《幼儿教师招聘》模拟题库附参考答案详解【夺分金卷】
- 单元考点必刷卷 (一)(含答案)我上学啦 2025-2026学年北师大版一年级数学上册
- 2025保安员考试基础知识应知应会试题+答案
- 2025-2026学年人教版(2024)小学体育与健康三年级(全一册)教学设计(附目录P114)
- 河南省天一联考2026届高三年级开学联考语文试卷(含答案解析)
- 遴选笔试真题及答案
- 2025年消防经济学试题及答案
- 2025-2026学年人教版(2024)小学美术三年级上册教学计划及进度表
- 2025年秋期新教材人音版三年级上册小学音乐教学计划+进度表
- 医疗科室外包合同协议书
- 基于核心素养的中小学安全教育课程设计与实施路径
- 2025年医院安全员安全技能测试
评论
0/150
提交评论