当前位置:  编程技术>c/c++/嵌入式
本页文章导读:
    ▪C++新手之详细介绍MFC      我们都知道MFC是C++类库,本文详细介绍C++的这个类库,适合初学者阅读,希望对你有帮助,一起来看。 MFC (Microsoft Foundation Class Library)中的各种类结合起来构成了一个应用程序框架,它的.........
    ▪基类和派生类的赋值规则      (在公有继承方式下:派生类的对象/对象指针/对象引用可以赋值给基类的对象/对象指针/对象引用,基类的对象/对象指针/对象引用不能赋值给派生类的对象/对象指针/对象引用。因为派生类包.........
    ▪翻译:怎样理解 C++ 11中的move语义(基础)--- An answer from stackoverflow      前不久facebook在github上发布了一个c++工具库folly,其中的实现大量的使用了c++ 11的新特性,同时,gcc 从4.3版本开始支持c++ 11, 到现在的版本4.8,已经支持了绝大部分c++ 11的新特性(support list),让.........

[1]C++新手之详细介绍MFC
    来源:    发布时间: 2013-10-17

我们都知道MFC是C++类库,本文详细介绍C++的这个类库,适合初学者阅读,希望对你有帮助,一起来看。

 

MFC (Microsoft Foundation Class Library)中的各种类结合起来构成了一个应用程序框架,它的目的就是让程序员在此基础上来建立Windows下的应用程序,这是一种相对SDK来说更为简单的方法。

因为总体上,MFC框架定义了应用程序的轮廓,并提供了用户接口的标准实现方法,程序员所要做的就是通过预定义的接口把具体应用程序特有的东西填入这个轮廓。

Microsoft Visual C++提供了相应的工具来完成这个工作:AppWizard可以用来生成初步的框架文件(代码和资源等);资源编辑器用于帮助直观地设计用户接口;ClassWizard用来协助添加代码到框架文件;最后,编译,则通过类库实现了应用程序特定的逻辑。

一. 封装

构成MFC框架的是MFC类库。MFC类库是C++类库。这些类或者封装了Win32应用程序编程接口,或者封装了应用程序的概念,或者封装了OLE特性,或者封装了ODBC和DAO数据访问的功能,等等,分述如下。

(1)对Win32应用程序编程接口的封装

用一个C++ Object来包装一个Windows Object。例如:class CWnd是一个C++ window object,它把Windows window(HWND)和Windows window有关的API函数封装在C++ window object的成员函数内,后者的成员变量m_hWnd就是前者的窗口句柄。

(2)对应用程序概念的封装

使用SDK编写Windows应用程序时,总要定义窗口过程,登记Windows Class,创建窗口,等等。MFC把许多类似的处理封装起来,替程序员完成这些工作。另外,MFC提出了以文档-视图为中心的编程模式,MFC类库封装了对它的支持。文档是用户操作的数据对象,视图是数据操作的窗口,用户通过它处理、查看数据。

(3)对COM/OLE特性的封装

OLE建立在COM模型之上,由于支持OLE的应用程序必须实现一系列的接口(Interface),因而相当繁琐。MFC的OLE类封装了OLE API大量的复杂工作,这些类提供了实现OLE的更高级接口。

(4)对ODBC功能的封装

以少量的能提供与ODBC之间更高级接口的C++类,封装了ODBC API的大量的复杂的工作,提供了一种数据库编程模式。

二、 继承

首先,MFC抽象出众多类的共同特性,设计出一些基类作为实现其他类的基础。这些类中,最重要的类是CObject和CCmdTarget。CObject 是MFC的根类,绝大多数MFC类是其派生的,包括CCmdTarget。CObject 实现了一些重要的特性,包括动态类信息、动态创建、对象序列化、对程序调试的支持,等等。所有从CObject派生的类都将具备或者可以具备 CObject所拥有的特性。CCmdTarget通过封装一些属性和方法,提供了消息处理的架构。MFC中,任何可以处理消息的类都从 CCmdTarget派生。

针对每种不同的对象,MFC都设计了一组类对这些对象进行封装,每一组类都有一个基类,从基类派生出众多更具体的类。这些对象包括以下种类:窗口对象,基类是CWnd;应用程序对象,基类是CwinThread;文档对象,基类是Cdocument,等等。

程序员将结合自己的实际,从适当的MFC类中派生出自己的类,实现特定的功能,达到自己的编程目的。

三、 虚拟函数和动态约束

MFC 以“C++”为基础,自然支持虚拟函数和动态约束。但是作为一个编程框架,有一个问题必须解决:如果仅仅通过虚拟函数来支持动态约束,必然导致虚拟函数表过于臃肿,消耗内存,效率低下。例如,CWnd封装 Windows窗口对象时,每一条Windows消息对应一个成员函数,这些成员函数为派生类所继承。如果这些函数都设计成虚拟函数,由于数量太多,实现起来不现实。于是,MFC建立了消息映射机制,以一种富有效率、便于使用的手段解决消息处理函数的动态约束问题。

这样,通过虚拟函数和消息映射,MFC类提供了丰富的编程接口。程序员继承基类的同时,把自己实现的虚拟函数和消息处理函数嵌入MFC的编程框架。MFC编程框架将在适当的时候、适当的地方来调用程序的代码。本书将充分的展示MFC调用虚拟函数和消息处理函数的内幕,让读者对MFC的编程接口有清晰的理解。

四、 MFC的宏观框架体系

如前所述,MFC实现了对应用程序概念的封装,把类、类的继承、动态约束、类的关系和相互作用等封装起来。这样封装的结果对程序员来说,是一套开发模板(或者说模式)。针对不同的应用和目的,程序员采用不同的模板。例如,SDI应用程序的模板,MDI应用程序的模板,规则DLL应用程序的模板,扩展DLL应用程序的模板,OLE/ACTIVEX应用程序的模板,等等。

这些模板都采用了以文档-视为中心的思想,每一个模板都包含一组特定的类。典型的MDI应用程序的构成将在下一节具体讨论。

为了支持对应用程序概念的封装,MFC内部必须作大量的工作。例如,为了实现消息映射机制,MFC编程框架必须要保证首先得到消息,然后按既定的方法进行处理。又如,为了实现对DLL编程的支持和多线程编程的支持,MFC内部使用了特别的处理方法,使用模块状态、线程状态等来管理一些重要信息。虽然,这些内部处理对程序员来说是透明的,但是,懂得和理解MFC内部机制有助于写出功能灵活而强大的程序。

总之,MFC封装了Win32 API,OLE API,ODBC API等底层函数的功能,并提供更高一层的接口,简化了Windows编程。同时,MFC支持对底层API的直接调用。

MFC提供了一个Windows应用程序开发模式,对程序的控制主要是由MFC框架完成的,而且MFC也完成了大部分的功能,预定义或实现了许多事件和消息处理,等等。框架或者由其本身处理事件,不依赖程序员的代码;或者调用程序员的代码来处理应用程序特定的事件。

MFC是C++类库,程序员就是通过使用、继承和扩展适当的类来实现特定的目的。例如,继承时,应用程序特定的事件由程序员的派生类来处理,不感兴趣的由基类处理。实现这种功能的基础是C++对继承的支持,对虚拟函数的支持,以及MFC实现的消息映射机制。

希望通过以上对MFC的介绍,能给大家带来帮助。

本文链接


    
[2]基类和派生类的赋值规则
    来源:    发布时间: 2013-10-17
(在公有继承方式下:
  • 派生类的对象/对象指针/对象引用可以赋值给基类的对象/对象指针/对象引用,基类的对象/对象指针/对象引用不能赋值给派生类的对象/对象指针/对象引用。因为派生类包含了基类的所有信息,而基类缺乏派生类中的信息。

        True      基类对象/对象指针/对象引用 = 派生类对象/对象指针/对象引用

        False     派生类对象/对象指针/对象引用 = 基类对象/对象指针/对象引用

      (1)公有派生类的对象可以赋值给基类的对象,即将公有派生类对象中从基类继承而来的数据成员逐个赋值给基类对象的对应数据成员;

      (2)公有派生类对象的地址可以赋值给基类的对象指针;

      (3)公有派生类对象可以用来初始化基类的对象引用。

  • C++容许把基类的对象指针强制转换(显式)成派生类的对象指针,使得基类对象指针可用来指向它公有派生类的任何对象,甚至显式地转换成派生类指针用来替代这些派生类对象去访问它们的新增成员。

          派生类对象/对象指针/对象引用 = (派生类名 *)基类对象/对象指针/对象引用

  • 一个指向基类的指针可以用来指向该基类公有派生类的任何对象,这是C++实现程序运行时多态性的关键途径:任何使用基类对象/指针/引用作为形参的函数,都可以用来处理派生类的对象:

          函数原型: <返回类型> 函数名(基类名 *指针名,……);

          调用语句: 函数名(&公有派生类对象名,……)

 

          函数原型: <返回类型> 函数名(基类名 &引用名,……);

          函数原型: <返回类型> 函数名(基类名  对象名,……);

          调用语句: 函数名(公有派生类对象名,……)

对于采用公有继承方式的类层次结构,若要编写一个函数处理其中某一层对象,应将该函数的形参指定为该类层次结构顶层基类的对象,或对象指针,或对象引用,能将该函数的使用范围扩大到整个类层次结构的任何一层。

 

下列有关继承和派生的叙述中,正确的是(D)。
A. 如果一个派生类私有继承其基类,则该派生类对象不能访问基类的保护成员
B. 派生类的成员函数可以访问基类的所有成员
C. 基类对象可以赋值给派生类对象
D. 如果派生类没有实现基类的一个纯虚函数,则该派生类是一个抽象类

本文链接


    
[3]翻译:怎样理解 C++ 11中的move语义(基础)--- An answer from stackoverflow
    来源:    发布时间: 2013-10-17

前不久facebook在github上发布了一个c++工具库folly,其中的实现大量的使用了c++ 11的新特性,同时,gcc 从4.3版本开始支持c++ 11, 到现在的版本4.8,已经支持了绝大部分c++ 11的新特性(support list),让我感到时候有必要认真学习一下c++ 11了.关于11引进的新特性概述,已经有很多的文章了,如果你不了解,可以到这里.

今天主要来学习11版本中颇为重要的一个特性,move语义与右值引用,Stackoverflow 上有一篇相当不错的解释(原文),我觉得十分有必要翻译一下,一方面自我学习,一方面分享给大家。由于原文较长,这里分为基础和加深分两部分来翻译,对应作者的2个回答。以下是基础部分(加深部分会在三天内译出):

-----------------------------------------------------------------译文

我发现理解move语义最简单的方式是看一个样例,让我们从持有一块动态分配的内存的简单string类型开始:

1 #include <cstring>
2 #include <algorithm>
3
4 class string
5 {
6 char* data;
7
8 public:
9
10 string(const char* p)
11 {
12 size_t size = strlen(p) + 1;
13 data = new char[size];
14 memcpy(data, p, size);
15 }

既然我们要自己管理内存,那我们就应该遵守那三条原则(the rule of three),如果你不知道什么三条原则,去c++ 98标准里查找。下面我将推迟赋值运算符的实现,先来实现复制构造函数和析构函数:

1 ~string()
2 {
3 delete[] data;
4 }
5
6 string(const string& that)
7 {
8 size_t size = strlen(that.data) + 1;
9 data = new char[size];
10 memcpy(data, that.data, size);
11 }

复制构造函数定义了怎样复制一个string对象。参数const string& that 可以为想要复制string的以下例子中的任何一种形式:

1 string a(x); // line 1
2 string b(x + y); // line 2
3 string c(some_function_returning_a_string()); // line 3

现在我们开始分析move语义。你会发现,只有第一行(line 1)的x深度拷贝是有必要的,因为我们可能会在后边用到x,如果x改变了,我们会很奇怪。你有没有注意到我刚刚把x说了三遍(如果包括这次的话,是四遍),每一遍都是说的同一个对象?我们把x的这种表达式叫做左值(lvalues)。

第二行和第三行的参数就不是左值而是右值,因为表达式产生的string对象是匿名对象,之后没有办法再使用了。右值就是指在下一个分号(更准确的说是在包含右值的完整表达式的最后)销毁的临时对象。这一点非常重要,因为我们可以在b和c的初始化过程中,对源string对象(参数)做任何想要做的事情,并不让用户感觉到。

C++ 11引入了一种新的机制叫做“右值引用”,以便我们通过重载直接使用右值参数。我们所要做的就是写一个以右值引用为参数的构造函数。在这个函数的内部,我们对参数所指向的对象做任何事情,只要我们保持他的合理性:

1 string(string&& that) // string&& is an rvalue reference to a string
2 {
3 data = that.data;
4 that.data = 0;
5 }

我们在这里是怎么做的呢?我们没有深度拷贝堆内存中的数据,而是仅仅复制了指针,并把源对象的指针置空。事实上,我们“偷取”了属于源对象的内存数据。再次,问题的关键变成:无论在任何情况下,都不能让客户觉察到源对象被改变了。在这里,我们并没有真正的复制,所以我们把这个构造函数叫做“转移构造函数”(move constructor, 不知道译法是否确切),他的工作就是把资源从一个对象转移到另一个对象,而不是复制他们。

祝贺你,你现在对move语义有了基础的理解,我们继续来进行赋值操作符的实现。如果你不理解copy and swap惯用法,先去学习 一下,然后再回来,因为这是c++异常安全的一个非常精彩的惯用法。

1 string& operator=(string that)
2 {
3 std::swap(data, that.data);
4 return *this;
5 }
6 };

呃,就这些?右值引用在哪里?你可能会这样问。我的答案是:在这里,我们不需要

注意到我们是直接对参数that传值,所以that会像其他任何对象一样被初始化,那么确切的说,that是怎样被初始化的呢?对于C++ 98,答案是复制构造函数,但是对于C++ 11,编译器会依据参数是左值还是右值在复制构造函数和转移构造函数间进行选择。

如果是a=b,这样就会调用复制构造函数来初始化that(因为b是左值),赋值操作符会与新创建的对象交换数据,深度拷贝。这就是copy and swap 惯用法的定义:构造一个副本,与副本交换数据,并让副本在作用域内自动销毁。这里也一样。

如果是a = x + y,这样就会调用

    
最新技术文章:
▪C++单例模式应用实例
▪C++设计模式之迭代器模式
▪C++实现动态分配const对象实例
▪C++设计模式之中介者模式
▪C++设计模式之备忘录模式
▪C++插入排序算法实例
▪C++冒泡排序算法实例
▪C++选择排序算法实例
▪C++归并排序算法实例
▪C++设计模式之观察者模式
▪C++中复制构造函数和重载赋值操作符总结
▪C++设计模式之状态模式
▪C++设计模式之策略模式
▪C++设计模式之访问者模式
▪C++设计模式之模板方法模式
▪C++实现下载的代码
▪C++模板之特化与偏特化详解
▪C++实现查壳程序代码实例
▪C语言、C++内存对齐问题详解
▪C语言、C++中的union用法总结
▪C++基于CreateToolhelp32Snapshot获取系统进程实例
▪C++中memcpy和memmove的区别总结
▪C++通过TerminateProess结束进程实例
▪C++内存查找实例
▪C++实现CreatThread函数主线程与工作线程交互的...
▪C++设计模式之桥接模式
▪C++中关键字Struct和Class的区别
▪C++设计模式之组合模式
▪C++ COM编程之什么是组件?
▪C++ COM编程之什么是接口?
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3