MFC常见面试问题(持续更新)

1)说说mfc中的类继承图的基本框架,

http://new.51cto.com/files/uploadimg/20090513/154244497.jpg

 MFC的类层次(

)

MFC(微软基础类)也是一种应用程序框架,定义了应用程序的一般处理流程,用于对Windows API实现基于面向对象技术的封装,隐藏在Windows下使用C++编程的大量内部细节。在开发应用程序的过程中,编程人员可以通过对类库中已有类的继承,生成功能更加强大的类库以供自己所用。

在MFC中类的层次结构(即继承关系)如图5-3所示。


从图中可知,在MFC中大多数的类都派生于CObject类,它的主要作用是为子类提供一些基本的功能,这些派生类构成了MFC应用程序的基本框架,它们各自的功能描述如表5-1所示。

派生类

功能描述

CCmdTarget

用于处理用户请求

CWinThread

代表应用程序内部的执行线程

CWinApp

应用程序的核心

CWnd

为所有的窗口类提供基本的功能,处理常见的系统消息

CView

用于显示数据并与文档对象进行交互

CFrameWnd

应用程序的主框架

CDocument

包含应用程序的数据集

下面将对上述表中各个类的功能进行具体的讲解。

1)CCmdTarget类

CCmdTarget类是MFC的消息映射基础类,MFC为该类设计了许多的成员变量及函数以解决消息映射的问题。派生于CCmdTarget的类可用于处理当用户选择菜单或单击按钮等操作时所产生的Command消息。

在实际的开发过程中,我们通常很少直接从CCmdTarget中派生类。当想要生成一个处理按键消息的类时,只需从继承于CCmdTarget类的框架子类CView、CWinApp、CDocument、CWnd和CFrameWnd中选择一个来充当父类即可。

2)CWinThread类

CWinThread类是MFC中用于封装线程的类,它的成员函数可以使MFC应用程序创建和管理包括UI及工作者在内的线程。每个MFC应用程序都至少应该使用一个从CWinThread派生的类,应用程序类CWinApp就是一个代表。

3)CWinApp类

CWinApp类通常代表应用程序自己,它封装了应用程序的初始化、运行及终止的过程。基于框架的应用程序必须有且仅有一个派生于CWinApp的类的对象,并在完成窗口的创建工作之前执行对该对象的构造。

应用程序类的对象需要完成以下工作。

初始化应用程序。

建立文档模板结构。

循环检索消息队列中的消息并将这些消息发送到指定的地方。

执行应用程序退出时的清理工作。

4)CDocument类

CDocument类是在使用文档/视图结构的应用程序中文档对象的基类,它为应用程序的文档对象提供了基本的功能,包括新建、串行化数据等。

5)CWnd类

CWnd类是所有MFC 窗口的基类,它封装了窗口的基本操作,包括窗口的创建、销毁、设置窗口风格等,以及窗口对大部分消息的默认响应。开发人员可以直接从CWnd派生其他类,但通常情况下我们并不这么做,而是通过继承CWnd的派生类生成新类。

6)CFrameWnd类

CFrameWnd类往往用于创建应用程序的主窗口,并定义了大量管理视图和文档对象的成员函数及变量。在编写文档/视图结构的应用程序时,视图对象等将作为CFrameWnd的子窗口实现对客户区的共享,并被CFrameWnd有序排列。

7)CView类

CView类是在使用文档/视图结构的应用程序中视图对象的基类,它是用户的主要操作界面。在应用程序中,一个视图对象通常只对应一个文档对象,但一个文档对象却可以关联多个视图对象,并且每个视图对象都以不同的形式来显示文档中的数据。

在上述CObject类的派生类中,CWinApp类、CDocument类、 CCmdTarget类及CWinThread类构成了应用程序的结构类,代表了应用程序的基本结构元素。换句话说,当一个应用程序开始运行时,这些类将最先实现初始化。

在类的层次结构中,应用程序类CWinApp是一个基于MFC应用程序的最外层对象容器,它不仅拥有诸如实例句柄等需要被传送到WinMain()函数中去的参数,还包含了应用程序的主框架窗口,当主框架窗口被关闭时,应用程序也就跟着结束了。因此,开发人员必须为程序创建一个全局的应用程序对象。



2)说说CView类的子类都有什么。

视图类(CView)的三个子类
CScrollView类提供视图的滚动显示;CEditView类支持在视图中的文本编辑操作;CHtmlView类支持在视图中显示和操作html文件。

3)DLL的三种调用形式。

DLL的概念

可以向程序提供一些函数、变量或 类 。

静态链接库与动态链接库的区别:
(1)静态链接库与动态链接库 都是共享代码的方式 。静态链接库把最后的指令 都包含 在最终生成的EXE文件中了;动态链接库不必被包含在最终EXE文件中,EXE文件 执行时可以“动态”地引用和卸载 这个与EXE独立的DLL文件。
(2) 静态链接库中 不能再包含其他的动态链接库或者静态库 ,而在动态链接库中还可以再包含其他的动态或静态链接库。

DLL分类:
1。Non-MFC DLL(非MFC动态库):不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;

2。MFC Regular DLL(MFC规则DLL):非MFC动态库MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;

3。MFC Extension DLL(MFC扩展DLL):采用MFC的动态链接版本创建,它 只能被用MFC类库所编写的应用程序所调用。


4)说说onpaint()和ondraw()的关系。
 
问题:我在视图画的图象或者文字,当窗口改变后为什么不见了?OnDraw()和OnPaint()两个都是解决上面的问题,有什么不同?
答:OnDraw()和OnPaint()好象兄弟俩,因为它们的工作类似。
至于不见了的问题简单,因为当你的窗口改变后,会产生无效区域,这个无效的区域需要重画。一般Windows会发送两个消息WM_PAINT(通知客户区 有变化)和WM_NCPAINT(通知非客户区有变化)。非客户区的重画系统自己搞定了,而客户区的重画需要我们自己来完成。这就需要OnDraw()或 OnPaint()来重画窗口。

OnDraw()和OnPaint()有什么区别呢?首先:我们先要明确CView类派生自CWnd类。而OnPaint()是CWnd的类成员,同时负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数,并且没有响应消息的功能。这就是为什么你用VC成的程序代码时,在视图类只有 OnDraw没有OnPaint的原因。

其次,要想在屏幕上绘图或显示图形,首先需要建立设备环境DC。其实DC是一个数据结构,它包含输 出设备(不单指你17寸的纯屏显示器,还包括打印机之类的输出设备)的绘图属性的描述。MFC提供了CPaintDC类和CWindwoDC类来实时的响 应,而CPaintDC支持重画。

当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的DC对象来响应该消息并调用视图的 OnDraw 成员函数。通常我们不必编写重写的 OnPaint 处理成员函数。

///CView默认的标准的重画函数

void CView::OnPaint()
    CPaintDC dc(this);
    OnPreparDC(&dc);
    OnDraw(&dc); //调用了OnDraw
}


既然OnPaint最后也要调用OnDraw,因此我们一般会在OnDraw函数中进行绘制。下面是一个典型的程序

///视图中的绘图代码首先检索指向文档的指针,然后通过DC进行绘图调用。
void CMyView::OnDraw( CDC* pDC )
    CMyDoc* pDoc = GetDocument(); 
    CString s = pDoc->GetData(); // Returns a CString
    CRect rect;
    GetClientRect( &rect ); 
    pDC->SetTextAlign( TA_BASELINE | TA_CENTER ); 
    pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );
}
最后:现在大家明白这哥俩之间的关系了吧。因此我们一般用OnPaint维护窗口的客户区(例如我们的窗口客户区加一个背景图片),用OnDraw维护视图的客户区(例如我们通过鼠标在视图中画图)。当然你也可以不按照上面规律来,只要达到目的并且没有问题,怎么干都成。

补充:我们还可以利用Invalidate(),ValidateRgn(),ValidateRect()函数强制的重画窗口
  
5)说说CView类与CDocument的关系(重要)。

CView有一个成员变量CDocument* m_pDocument,指向相关的Documente.
  • Document和view交谈过程:

    1 使用者在Viewl做动作(View扮演使用者接口的第一线)。
    2 Viewl调用GetDocument,取得Document指针,更改资料内容。
    3 Viewl调用Document的UpdateAllViews。
    4 View2和View3的OnUpdate一一被调用起来,这是更新画面的时机。

    CView::OnUpdate被调用,代表着View被告知:“嘿,Document的内容己经改变了,请你准备修改你的显示画面”。如果你想节省力气,利用Invalidate(TRUE)把窗口整个设为重绘区(无效区)并产生WM_PAINT,再让CView::OnDraw去伤脑筋算了。但是全部重绘的效率低落,程序看起来很笨拙。


6)说SendMessage()与PostMessage()的区别。

PostMessage和SendMessage的区别
 
1, PostMessage只把消息放入队列,不管其他程序是否处理都返回,然后继续执行,这是个异步消息投放函数。而SendMessage必须等待其他程序处理消息完了之后才返回,继续执行,这是个同步消息投放函数。而且,PostMessage的返回值表示PostMessage函数执行是否正确;而SendMessage的返回值表示其他程序处理消息后的返回值。这点大家应该都明白。
2, 如果在同一个线程内,PostMessage发送消息时,消息要先放入线程的消息队列,然后通过消息循环Dispatch到目标窗口。SendMessage发送消息时,系统直接调用目标窗口的消息处理程序,并将结果返回。SendMessage在同一线程中发送消息并不入线程消息队列。 如果在不同线程内。最好用PostThreadMessage代替PostMessage,他工作的很好。SendMessage发送消息到目标窗口所属的线程的消息队列,然后发送消息的线程等待(事实上,他应该还在做一些监测工作,比如监视QS_SENDMESSAGE标志),直到目标窗口处理完并且结果返回,发送消息的线程才继续运行。这是SendMessage的一般情况,事实上,处理过程要复杂的多。比如,当发送消息的线程监测到有别的窗口SendMessage一个消息到来时,他直接调用窗口处理过程(重入),并将处理结果返回(这个过程不需要消息循环中GetMessage等的支持)。
3, msdn: If you send a message in the range below WM_USER to the asynchronous message functions (PostMessage, SendNotifyMessage, and SendMessageCallback), its message parameters can not include pointers. Otherwise, the operation will fail. 如果发送的消息码在WM_USER之下(非自定义消息)且消息参数中带有指针,那么PostMessage,SendNotifyMessage,SendMessageCallback这些异步消息发送函数将会调用失败。 最好不要用PostMessage发送带有指针参数的消息。

 

7)简述COM技术。

COM(Component Object Model,组件对象模型),是由微软推出的一套接口规范,通过设定不同组件之间需要遵守的标准与协议,主要用来跨语言、跨进程之间的模块通信。所谓COM(Component Object Model,组件对象模型)是一种说明如何建立可动态互变组件的规范,此规范提供了为保证能够互操作,客户和组件应遵循的一些二进制网络标准。通过这种标准将可以在任意两个组件之间进行通信而不用考虑其所处的操作环境是否相同、使用的开发语言是否一致以及是否运行于同一台计算机。

8)说说读写一个文件怎么来完成,分别用c,c++,MFC三种库函数来编写代码。

对文件读写的三种方法
  1.C中
    FILE *pFile=fopen("1.txt","w");
fwrite("http://www.sunxin.org",1,strlen("http://www.sunxin.org"),pFile");
//fseek(pFile,0,SEEK_SET);
//fwrite("ftp:",1,strlen("ftp:"),pFile);
//fwrite("http://www.sunxin.org",1,strlen("http://www.sunxin.org"),pFile");
fclose(pFile);*/
//fflush(pFile);

表头文件 #include<stdio.h>

FILE * fopen(const char * path,const char * mode);
  函数说明 参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
  mode有下列几种形态字符串:

int fseek( FILE *stream, long offset, int origin );
  第一个参数stream为文件指针
  第二个参数offset为偏移量,整数表示正向偏移,负数表示负向偏移
  第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
  SEEK_CUR: 当前位置
  SEEK_END: 文件结尾
  SEEK_SET: 文件开头
  其中SEEK_CUR,SEEK_END和SEEK_SET依次为1,2和0

:fwrite(buffer,size,count,fp);
  (1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。
  (2)size:要写入的字节数;
  (3)count:要进行写入size字节的数据项的个数;
  (4)fp:目标文件指针。

int fread(void *ptr, int size, int nitems, FILE *stream);
  参 数:用于接收数据的地址(指针)(ptr)
  单个元素的大小(size)
  元素个数(nitems)
  提供数据的文件指针(stream)

:int fflush(FILE *stream) 
         功 能: 清除文件缓冲区,文件以写方式打开时将缓冲区内容写入文件 

  2.C++中
/* ofstream ofs("4.txt");
ofs.write("http://www.sunxin.org",strlen("http://www.sunxin.org"));
ofs.close();*/
         要包括头文件 "fstream.h"

  3.MFC中 用CFile类,哈哈!简单好用
CFileDialog fileDlg(FALSE);
fileDlg.m_ofn.lpstrTitle="我的文件保存对话框";
fileDlg.m_ofn.lpstrFilter="Text Files(*.txt)\0*.txt\0All Files(*.*)\0*.*\0\0";
fileDlg.m_ofn.lpstrDefExt="txt";
if(IDOK==fileDlg.DoModal())
{
  CFile file(fileDlg.GetFileName(),CFile::modeCreate | CFile::modeWrite);
  file.Write("http://www.sunxin.org",strlen("http://www.sunxin.org"));
  file.Close();
}

9)说说二进制文件和文本文件之间区别,举例子,会吗?

作者:中正
链接:https://www.zhihu.com/question/19971994/answer/36121103
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

广义上的二进制文件包括文本文件,这里讨论的是狭义上的二进制文件与文本文件的比较:

1. 能存储的数据类型不同
文本文件只能存储char型字符变量。
二进制文件可以存储char/int/short/long/float/……各种变量值。

2. 每条数据的长度
文本文件每条数据通常是固定长度的。以ASCII为例,每条数据(每个字符)都是1个字节。
二进制文件每条数据不固定。如short占两个字节,int占四个字节,float占8个字节……

3. 读取的软件不同
文本文件编辑器就可以读写。比如记事本、NotePad++、Vim等。
二进制文件需要特别的解码器。比如bmp文件需要图像查看器,rmvb需要播放器……

4. 操作系统对换行符('\n')的处理不同 (不重要)
文本文件,操作系统会对'\n'进行一些隐式变换,因此文本文件直接跨平台使用会出问题。
  • 在Windows下,写入'\n'时,操作系统会隐式的将'\n'转换为"\r\n",再写入到文件中;读的时候,会把“\r\n”隐式转化为'\n',再读到变量中。
  • 在Linux下,写入'\n'时,操作系统不做隐式变换。

二进制文件,操作系统不会对'\n'进行隐式变换,很多二进制文件(如电影、图片等)可以跨平台使用。


10).net的技术优点是何?

一:什么是.NET?它包括什么?

.Net是为简化在第三代因特网的高分布式环境下的应用程序开发,基于开放互联网标准和协议之上,实现异质语言和平台高度交互性,而构建的新一代计算和通信平台。

.Net主要包括公共语言运行时(CommonLanguage Runtime)和.Net构架类库

 

二: .NET的主要优点有哪些?

   .Net的主要优点有跨语言,跨平台,安全,以及对开放互联网标准和协议的支持.

   <a>.Net支持多种语言的互操作,即在一种语言下开发的组件,可在另一组件下通过面向对象的继承而得

      以重用,目前.Net支持的语言达二十多种。

    <b>.Net通过将各语言先编译成中间语言(IL),然后再执行时用即时编译器(Just In Time)将之编译成本

       地平台代码来实现异构平台下对象的互操作,目前.Net支持的平台有Windows,LinuxUnix的支持正

       在开发中。 

    <c>.Net通过公共语言运行时(Common LanguageRuntime)来实现资源对象,类型的安全.

   <d>.Net通过对HTTP,XMLSOAPWSDLInternet标准的强劲支持提供在异构网络环境下获取远程服务,

      连接远程设备,交互远程应用的编程界面.


11).关于MFC的消息机制。

MFC使用一种消息映射机制来处理消息,在应用程序框架中的表现就是一个消息与消息处理函数一一对应的消息映射表,以及消息处理函数的声明和实现等代码。当窗口接收到消息时,会到消息映射表中查找该消息对应的消息处理函数,然后由消息处理函数进行相应的处理。SDK编程时需要在窗口过程中一一判断消息值进行相应的处理,相比之下MFC的消息映射机制要方便好用的多。


12).进程/线程间的通信

线程间通信:由于多线程共享地址空间和数据空间,所以多个线程间的通信是一个线程的数据可以直接提供给其他线程使用,而不必通过操作系统(也就是内核的调度)。

进程间的通信则不同,它的数据空间的独立性决定了它的通信相对比较复杂,需要通过操作系统。以前进程间的通信只能是单机版的,现在操作系统都继承了基于套接字(socket)的进程间的通信机制。这样进程间的通信就不局限于单台计算机了,实现了网络通信。

        进程的通信机制主要有:管道、有名管道、消息队列、信号量、共享空间、信号、套接字。

        管道:它传递数据是单向性的,只能从一方流向另一方,也就是一种半双工的通信方式;只用于有亲缘关系的进程间的通信,亲缘关系也就是父子进程或兄弟进程;没有名字并且大小受限,传输的是无格式的流,所以两进程通信时必须约定好数据通信的格式。管道它就像一个特殊的文件,但这个文件之存在于内存中,在创建管道时,系统为管道分配了一个页面作为数据缓冲区,进程对这个数据缓冲区进行读写,以此来完成通信。其中一个进程只能读一个只能写,所以叫半双工通信,为什么一个只能读一个只能写呢?因为写进程是在缓冲区的末尾写入,读进程是在缓冲区的头部读取,他们各自 的数据结构不同,所以功能不同。

        有名管道:看见这个名字就能知道个大概了,它于管道的不同的是它有名字了。这就不同与管道只能在具有亲缘关系的进程间通信了。它提供了一个路径名与之关联,有了自己的传输格式。有名管道和管道的不同之处还有一点是,有名管道是个设备文件,存储在文件系统中,没有亲缘关系的进程也可以访问,但是它要按照先进先出的原则读取数据。同样也是单双工的。

        消息队列:是存放在内核中的消息链表,每个消息队列由消息队列标识符标识,于管道不同的是,消息队列存放在内核中,只有在内核重启时才能删除一个消息队列,内核重启也就是系统重启,同样消息队列的大小也是受限制的。

        信号量:也可以说是一个计数器,常用来处理进程或线程同步的问题,特别是对临界资源的访问同步问题。临界资源:为某一时刻只能由一个进程或线程操作的资源,当信号量的值大于或等于0时,表示可以供并发进程访问的临界资源数,当小于0时,表示正在等待使用临界资源的进程数。更重要的是,信号量的值仅能由PV操作来改变。

        共享内存:就是分配一块能被其他进程访问的内存。共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。首先说下在使用共享内存区前,必须通过系统函数将其附加到进程的地址空间或说为映射到进程空间。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到 进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互 斥锁和信号量都可以。采用共享内存通信的一个显而易 见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而 共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就 解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存 中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

         信号:信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。信号事件的发生有两个来源:硬件来源(比如我们按下了键盘或者其它硬件故障);软件来源。信号分为可靠信号和不可靠信号,实时信号和非实时信号。进程有三种方式响应信号1.忽略信号2.捕捉信号3.执行缺省操作。

        套接字:套接字是通信端点的抽象,就是这个通信端点的逻辑代表。

13.)线程同步与互斥

在引入多线程后,由于线程执行的异步性,会给系统造成混乱,特别是在急用临界资源时,如多个线程急用同一台打印机,会使打印结果交织在一起,难于区分。当多个线程急用共享变量,表格,链表时,可能会导致数据处理出错,因此线程同步的主要任务是使并发执行的各线程之间能够有效的共享资源和相互合作,从而使程序的执行具有可再现性。

当线程并发执行时,由于资源共享和线程协作,使用线程之间会存在以下两种制约关系。

     (1)间接相互制约。一个系统中的多个线程必然要共享某种系统资源,如共享CPU,共享I/O设备,所谓间接相互制约即源于这种资源共享,打印机就是最好的例子,线程A在使用打印机时,其它线程都要等待。

     (2)直接相互制约。这种制约主要是因为线程之间的合作,如有线程A将计算结果提供给线程B作进一步处理,那么线程B在线程A将数据送达之前都将处于阻塞状态。

       间接相互制约可以称为互斥,直接相互制约可以称为同步,对于互斥可以这样理解,线程A和线程B互斥访问某个资源则它们之间就会产个顺序问题——要么线程A等待线程B操作完毕,要么线程B等待线程操作完毕,这其实就是线程的同步了。因此同步包括互斥,互斥其实是一种特殊的同步




©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页