1、运行时类型识别

运行时类型识别(RTTI)即是程序执行过程中知道某个对象属于某个类,我们平时用C++编程接触的RTTI一般是编译器的RTTI,即是在新版本的VC++编译器里面选用“使能RTTI”,然后载入typeinfo.h文件,就可以使用一个叫typeid()的运算子,它的地位与在C++编程中的sizeof()运算子类似的地方(包含一个头文件,然后就有一个熟悉好用的函数)。typdid()关键的地方是可以接受两个类型的参数:一个是类名称,一个是对象指针。所以我们判别一个对象是否属于某个类就可以象下面那样:

if (typeid (ClassName)== typeid(*ObjectName)){
((ClassName*)ObjectName)->Fun();
}

  象上面所说的那样,一个typeid()运算子就可以轻松地识别一个对象是否属于某一个类,但MFC并不是用typeid()的运算子来进行动态类型识别,而是用一大堆令人费解的宏。很多学员在这里很疑惑,好象MFC在大部分地方都是故作神秘。使们大家编程时很迷惘,只知道在这里加入一组宏,又在那儿加入一个映射,而不知道我们为什么要加入这些东东。

  其实,早期的MFC并没有typeid()运算子,所以只能沿用一个老办法。我们甚至可以想象一下,如果MFC早期就有template(模板)的概念,可能更容易解决RTTI问题。

  所以,我们要回到“古老”的年代,想象一下,要完成RTTI要做些什么事情。就好像我们在一个新型(新型到我们还不认识)电器公司里面,我们要识别哪个是电饭锅,哪个是电磁炉等等,我们要查看登记的各电器一系列的信息,我们才可以比较、鉴别,那个东西是什么!
要登记一系列的消息并不是一件简单的事情,大家可能首先想到用数组登记对象。但如果用数组,我们要定义多大的数组才好呢,大了浪费空间,小了更加不行。所以我们要用另一种数据结构——链表。因为链表理论上可大可小,可以无限扩展。

  链表是一种常用的数据结构,简单地说,它是在一个对象里面保存了指向下一个同类型对象的指针。我们大体可以这样设计我们的类:

struct CRuntimeClass
{
……类的名称等一切信息……
CRuntimeClass * m_pNextClass;//指向链表中下一CRuntimeClass对象的指针
};

  链表还应该有一个表头和一个表尾,这样我们在查链表中各对象元素的信息的时候才知道从哪里查起,到哪儿结束。我们还要注明本身是由哪能个类派生。所以我们的链表类要这样设计:

struct CRuntimeClass
{
……类的名称等一切信息……
CRuntimeClass * m_pBaseClass;//指向所属的基类。
CRuntimeClass * m_pNextClass;//定义表尾的时候只要定义此指针为空就可以 了。
static CRuntimeClass* pFirstClass;//这里表头指针属于静态变量,因为我们知道static变量在内存中只初始化一次,就可以为各对象所用!保证了各对象只有一个表头。
};

  有了CRuntimeClass结构后,我们就可以定义链表了:

static CRuntimeClass classCObject={NULL,NULL};//这里定义了一个CRuntimeClass对象,

  因为classCObject无基类,所以m_pBaseClass为NULL。因为目前只有一个元素(即目前没有下一元素),所以m_pNextClass为NULL(表尾)。

  至于pFirstClass(表头),大家可能有点想不通,它到什么地方去了。因为我们这里并不想把classCObject作为链表表头,我们还要在前面插入很多的CRuntimeClass对象,并且因为pFirstClass为static指针,即是说它不是属于某个对象,所以我们在用它之前要先初始化:

CRuntimeClass* CRuntimeClass::pFirstClass=NULL;

  现在我们可以在前面插入一个CRuntimeClass对象,插入之前我得重要申明一下:如果单纯为了运行时类型识别,我们未必用到m_pNextClass指针(更多是在运行时创建时用),我们关心的是类本身和它的基类。这样,查找一个对象是否属于一个类时,主要关心的是类本身及它的基类:

CRuntimeClass classCCmdTarget={ &classCObject, NULL};
CRuntimeClass classCWnd={ &classCCmdTarget ,NULL };
CRuntimeClass classCView={ &classCWnd , NULL };

  好了,上面只是仅仅为一个指针m_pBaseClass赋值(MFC中真正CRuntimeClass有多个成员变量和方法),就连接成了链表。假设我们现在已全部构造完成自己需要的CRuntimeClass对象,那么,这时候应该定义表头。即要用pFirstClass指针指向我们最后构造的CRuntimeClass对象——classCView。

CRuntimeClass::pFirstClass=&classCView;

  现在链表有了,表头表尾都完善了,问题又出现了,我们应该怎样访问每一个CRuntimeClass对象?要判断一个对象属于某类,我们要从表头开始,一直向表尾查找到表尾,然后才能比较得出结果吗。肯定不是这样!

  大家可以这样想一下,我们构造这个链表的目的,就是构造完之后,能够按主观地拿一个CRuntimeClass对象和链表中的元素作比较,看看其中一个对象中否属于你指定的类。这样,我们需要有一个函数,一个能返回自身类型名的函数GetRuntimeClass()。

  上面简单地说一下链表的过程,但单纯有这个链表是没有任何意义。回到MFC中来,我们要实现的是在每个需要有RTTI能力的类中构造一个CRuntimeClass对象,比较一个类是否属于某个对象的时候,实际上只是比较CRuntimeClass对象。

  如何在各个类之中插入CRuntimeClass对象,并且指定CRuntimeClass对象的内容及CRuntimeClass对象的链接,这里起码有十行的代码才能完成。在每个需要有RTTI能力的类设计中都要重复那十多行代码是一件乏味的事情,也容易出错,所以MFC用了两个宏代替这些工作,即DECLARE_DYNAMIC(类名)和IMPLEMENT_DYNAMIC(类名,基类名)。从这两个宏我们可以看出在MFC名类中的CRuntimeClass对象构造连接只有类名及基类名的不同!

  到此,可能会有朋友问:为什么要用两个宏,用一个宏不可以代换CRuntimeClass对象构造连接吗?个人认为肯定可以,因为宏只是文字代换的游戏而已。但我们在编程之中,头文件与源文件是分开的,我们要在头文件头声明变量及方法,在源文件里实具体实现。即是说我们要在头文件中声明:

public:
static CRuntimeClass classXXX //XXX为类名
virtual CRuntime* GetRuntimeClass() const;

  然后在源文件里实现:

CRuntimeClass* XXX::classXXX={……};
CRuntime* GetRuntimeClass() const;
{ return &XXX:: classXXX;}//这里不能直接返回&classXXX,因为static变量是类拥有而不是对象拥有。

  我们一眼可以看出MFC中的DECLARE_DYNAMIC(类名)宏应该这样定义:

#define DECLARE_DYNAMIC(class_name) public: static CRuntimeClass class##class_name;
virtual CRuntimeClass* GetRuntimeClass() const;

  其中##为连接符,可以让我们传入的类名前面加上class,否则跟原类同名,大家会知道产生什么后果。

  有了上面的DECLARE_DYNAMIC(类名)宏之后,我们在头文件里写上一句:

DECLARE_DYNAMIC(XXX)

  宏展开后就有了我们想要的:

public:
static CRuntimeClass classXXX //XXX为类名
virtual CRuntime* GetRuntimeClass() const;

  对于IMPLEMENT_DYNAMIC(类名,基类名),看来也不值得在这里代换文字了,大家知道它是知道回事,宏展开后为我们做了什么,再深究真是一点意义都没有!

  有了此链表之后,就像有了一张存放各类型的网,我们可以轻而易举地RTTI。CObject有一个函数BOOL IsKindOf(const CRuntimeClass* pClass) const;,被它以下所有派生员继承。

  此函数实现如下:

BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{
CRuntimeClass* pClassThis=GetRuntimeClass();//获得自己的CRuntimeClass对象指针。
while(pClassThis!=NULL)
{
if(pClassThis==pClass) return TRUE;
pClassThis=pClassThis->m_pBaseClass;//这句最关键,指向自己基类,再回头比较,一直到尽头m_pBaseClass为NULL结束。
}
return FALSE;
}

  说到这里,运行时类型识别(RTTI)算是完成了。

2、MFC模态对话框实现方式

在Windows操作系统中,面向用户的GUI基本上可分为对话框形式和文档/视图两种表现形式。对话框的显示方式又可分为模态对话框和非模态对话框,以适应不同的用户交互需求。由于对话框和文档/视图框架结构各有特色,能不能将文档/视图框架结构当作一对话框来使用,或在对话框中实现文档/视图框架结构内的特色功能呢,答案是肯定的。
  
  下面,从Windows 操作系统消息驱动机制开始,进而探讨模态对话框实现过程的消息封装、传递和处理机制,最后以模态的形式显示应用到文档/视图框架结构中的实例作为对所讲内容的验证和实践。
  
  一、Windows消息机制
  
  Windows是一种面向对象的体系结构,Windows环境和应用程序都是通过消息来交互的。Windows应用程序开始执行后,Windows为该程序创建一个"消息队列(message queue)",用以存放邮寄给该程序可能创建的各种不同窗口的消息。消息队列中消息的结构(MSG)为:
  
  typedef struct tagMSG{
  HWND hwnd;
  UINT message;
  WPARAM wParam;
  LPARAM lParam;
  DWORD time;
  POINT pt;
  }MSG;
  
  其中第一个成员变量是用来标识接收消息的窗口句柄;第二个参数便是消息标识号,如WM_PAINT;第三个和第四个参数的具体意义同message值有关,均为消息参数。前四个参数是非常重要和经常用到的,至于后两个参数则分别表示邮寄消息的时间和光标位置(屏幕坐标)。把消息传送到应用程序有两种方法:一种是由系统将消息"邮寄(post)"到应用程序的"消息队列"这是"进队消息"Win32 API有对应的函数: PostMessage(),此函数不等待该消息处理完就返回;而另一种则是由系统在直接调用窗口函数时将消息"发送(send)"给应用程序的窗口函数,属于"不进队消息"对应的函数是SendMessage()其必须等待该消息处理完后方可返回。
  
  对于每一个正在执行的Windows应用程序,系统为其建立一个"消息队列",即应用程序队列,用来存放该程序可能创建的各种窗口的消息。应用程序中含有一段称作"消息循环"的代码,用来从消息队列中检索这些消息并把它们分发到相应的窗口函数中。
  
  消息循环代码是应用程序中主函数winmain ( )中类似如下的程序段:
  
  while(GetMessage(&&msg,NULL,NULL,NULL))
  { file://从消息队列中取得消息
  TranslateMessage(&&msg);
  file://检索并生成字符消息WM_CHAR
  DispatchMessage(&&msg);
  file://将消息发送给相应的窗口函数
  }
  
  由此可见,所谓"消息循环",实际是程序循环。
  
  Windows 应用程序创建的每个窗口都在系统核心注册一个相应的窗口函数,窗口函数程序代码形式上是一个巨大的switch 语句,用以处理由消息循环发送到该窗口的消息,窗口函数由Windows 采用消息驱动的形式直接调用,而不是由应用程序显示调用的,窗口函数处理完消息后又将控制权返回给Windows。
  
  二、模态对话框的消息处理
  
  由上面我们看到,Windows是一个巨大的消息驱动结构,由用户发出消息,系统响应处理。非模态对话框是响应一个消息,系统处理一个消息,处理完毕后返回控制权给Windows。文档/视图框架结构与其类似。模态对话框在对话框创建后,挂起外部的消息,只是响应对话框内部的消息,而外部消息则全部"过滤"掉了,直到系统接收到WM_DESTROY或WM_CLOSE后,系统返回控制权给模态对话框创建前的线程,继续模态对话框创建前的线程将执行下面的代码。
  
  让我们看看下面的对话框DoModal实现代码:
  
  {
  … …
  // Disable 父窗口 (在创建对话框前)
  HWND hWndParent = PreModal();
  AfxUnhookWindowCreate();
  BOOL bEnableParent = FALSE;
  if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
  {
  ::EnableWindow(hWndParent, FALSE);
  bEnableParent = TRUE;
  }
  TRY
  {
  // 创建模态对话框
  AfxHookWindowCreate(this);
  if (CreateDlgIndirect(lpDialogTemplate,CWnd::FromHandle(hWndParent), hInst))
  {
  if (m_nFlags & WF_CONTINUEMODAL)
  {
  // 进入模式循环
  DWORD dwFlags = MLF_SHOWONIDLE;
  if (GetStyle() & DS_NOIDLEMSG) dwFlags |= MLF_NOIDLEMSG;
  VERIFY(RunModalLoop(dwFlags) == m_nModalResult);
  }
  }
  }
  CATCH_ALL(e)
  {
  DELETE_EXCEPTION(e);
  m_nModalResult = -1;
  }
  END_CATCH_ALL
  file://Enable 父窗口
  if (bEnableParent)
  ::EnableWindow(hWndParent, TRUE);
  if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
  ::SetActiveWindow(hWndParent);
  // 删除对话框
  DestroyWindow();
  PostModal();
  … …
  }
  
  可以看到,在此实现代码中,并没有开辟新的线程。系统是在RunModalLoop()中进行消息循环。当 m_nFlags 为 WF_CONTINUEMODAL时,系统继续模式状态。RunModalLoop()函数实际上也是一for(;;)循环,控制重新分派Windows消息。直到ContinueModal()返回FALSE,而当调用EndModalLoop()时,ContinueModal()返回FALSE。此时,标志着模态显示的结束。因此,实现模态对话框消息处理的核心部分为RunModalLoop()和EndModalLoop()函数。
  
  三、以模态的形式显示应用到文档/视图框架结构实例
  
  (1)新建一工程文件:ModeFrame,选取MFC AppWizard(exe)。
  
  (2)第二步选取Single document(单文档)。
  
  (3)其余几步均为缺省值。
  
  (4)用ClassWizard添加一新类CSubModeFrame,以CFrameWnd为基类。
  
  (5)添加CsubModeFrame的实现函数DoMode();
  
  int CsubModeFrame::DoModal()
  {
  HWND hWndParent = m_hWndPrt;
  CRect rc(0,0,400,400);
  CWnd *pParent = CWnd::FromHandle(hWndParent);
  DWORD dwStyle=WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_THICKFRAME |  WS_VISIBLE | WS_SYSMENU | WS_CAPTION;
  if(!Create(NULL,"模态文档/试图框架 ",dwStyle,rc,pParent,NULL)) return FALSE;
  BOOL bEnableParent = FALSE;
  if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
  {
  ::EnableWindow(hWndParent,FALSE);
  ::EnableWindow(m_hWnd,TRUE);
  bEnableParent = TRUE;
  }
  CenterWindow();
  TRY
  {
  // enter modal loop
  DWORD dwFlags = MLF_SHOWONIDLE;
  if (GetStyle() & DS_NOIDLEMSG) dwFlags |= MLF_NOIDLEMSG;
  VERIFY(RunModalLoop(dwFlags) == m_nModalResult);
  }
  CATCH_ALL(e)
  {
  DELETE_EXCEPTION(e);
  m_nModalResult = -1;
  }
  END_CATCH_ALL
  
  if (bEnableParent)
  ::EnableWindow(hWndParent, TRUE);
  if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
  ::SetActiveWindow(hWndParent);
  // destroy modal window
  DestroyWindow();
  return m_nModalResult;
  }
  
  (6)添加CsubModeFrame的实现函数EndMode()
  
  void CSubFrame::EndModal(){
  ASSERT(::IsWindow(m_hWnd));
  if (m_nFlags & (WF_MODALLOOP|WF_CONTINUEMODAL)) {
  EndModalLoop(1);
  }
  }
  
  (7)添加CModeFrameView的实现函数OnLButtonDblClk()
  
  在此函数的消息处理中:可以象显示对话框一样处理CsubModeFrame类。
  
  CSubModeFrame SubModeFrame;
  If(SubModeFrame.DoMode()){ MessageBox("Mode Ok");}
  
  (8)编译运行工程,双击视图,就会弹出模态的子文档/视图框架结构
  
  结论: 通过上面的分析和实例可以看出,深入研究了解Windows的消息处理机制,可利用消息对Windows的事件进行任意的定制和处理,不用拘泥于系统原有的模式。对进行深入Windows编程是很有必要的。

3、STL 适配器

1. 容器适配器概述

适配器(adaptor)是标准库中通用的概念,包括容器适配器、迭代器适配器和函数适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现,只是发生了接口转换而已。
标准库提供了三种顺序容器适配器:queue, priority_queue和stack。
所有适配器都定义了两个构造函数:默认构造函数用于创建空对象,而带一个容器参数的构造函数将参数容器的副本作为其基础值。
默认的stack和queue都基于deque容器实现,而priority_queue则在vector容器上实现。在创建适配器时,通过将一个顺序容器指定为适配器的第二个类型参数,可覆盖其关联的基础容器类型。例如:
stack<int, vector<int> > int_stack;  // 此时,int-stack栈是基于vector实现
对于给定的适配器,其关联的容器必须满足一定的约束条件。stack适配器所关联的基本容器可以是任意一种顺序容器类型,因为这些容器类型都提供了 push_back、pop_back和back操作;queue适配器要求其关联的基础容器必须提供pop_front操作,因此其不能建立在 vector容器上;priority_queue适配器要求提供随机访问功能,因此不能建立在list容器上。
两个相同类型的适配器可以做==, !=, <, >, <=, >=这些关系运算,只要其基本元素类型支持==和<两个操作即可。这与容器大小比较原则一致。
2. 栈
 s.empty()  如果栈为这人,则true;否则返回false
 s.size()  返回栈中元素的个数
 s.pop()  删除栈顶元素,但不返回其值
 s.top()  返回栈顶元素的值,但不删除该元素
 s.push(item)  在栈项压入新元素
3. 队列和优先级队列
标准库队列使用了先进先出(FIFO)的存储和检索策略,进入队列的元素被放置在尾部,下一个被取出的元素则取自队列的首部。
priority_queue默认使用元素类型的 < 操作符来确定它们之间的优先级关系,用户也可以定义自己的优先级关系。在优先级队列中,新元素被放置在比它优先级低的元素的前面。
 q.empty()  如果队列为空,则返回true;否则返回false
 q.size()  返回队列中元素的个数
 q.pop()  删除队首元素,但不返回其值
 q.front()  返回队首元素的值,但不删除该元素
 (注:该操作只适用于队列)
 q.back()  返回队尾元素的值,但不删除该元素
 (注:该操作只适用于队列)
 q.top()

返回具有最高优先级的元素值,但不删除该元素
 (注:该操作只适用于优先级队列。MSDN也为queue提供了该操作)

 q.push(item)  对于queue,在队尾压入一个新元素;
 对于priority_queue,在基于优先级的适当位置插入新元素

4、CEGUI消息事件机制

http://www.cppblog.com/tx7do/archive/2011/04/15/116954.html

11.10金山面试内容相关推荐

  1. 新年找工作的注意啦:一般通用面试内容【转】

    新年找工作的注意啦:一般通用面试内容 收藏 1.       您在选择工作中更看重的是什么? (可能是成长空间.培训机会.发挥平台.薪酬等答案)(若薪酬不排在第一,问)--您可不可以说说你在薪酬方面的 ...

  2. 最新B端产品经理面试问题大全及答案合集(151题全部有答案)【11年大厂面试官呕心制作】

    我会一直长期给你分享B端产品经理面试问题大全及答案合集,助你斩获心仪offer!请你去→工忠号[B端产品经理面试官Aadi],以免错失后续更多实用的B端产品经理面试技巧! 标题 B端产品经理面试问题大 ...

  3. 一个总经理的11个经典面试问题 (转载)

    一个总经理的11个经典面试问题 (转载) 孙子曰:"兵者,国之大事,死生之地,存亡之道,不可不察也."当代社会,应聘一个岗位,犹如与敌国打仗.准备打仗的时间与打仗时间之比应为10: ...

  4. 记录一位社招面试腾讯成功拿到offer的面试内容及收集的答案(上)

    //   以下记录 是为了 以后如果有机会面试大厂 起码了解一些 作为合格前端应该掌握的知识点  勉励自己 //   对了 还有平时 对用到的专有名词及缩写 请查清全称及读音 否则面试的时候 真的画面 ...

  5. 2021最新版JAVA开发面试那些事儿~(结尾有字节、华为面试内容)

    现在有越来越多的人进入互联网行业,但是各公司的面试花样也是越来越多,这里为大家整理了一些面试内容,供大家参考!(结尾有字节.华为面试内容) 一.北京神州新桥科技有限公司Java开发岗位 面试公司:北京 ...

  6. Ubuntu 11.10中用xen-tools安装虚拟机(UbuntuCentOS)

    Ubuntu 11.10中可以通过apt-get安装xen-tools,其版本为4.1版本: # apt-get install -y xen-tools 由于xen-tools是xen的半虚拟化管理 ...

  7. 安装UBUNTU Server 11.10

    第一次安装UBUNTU Server 11.10. 虽然是第一次用Linux,感觉是傻瓜式的安装,没啥特殊的地方.但是遇到了两个难点. 1.文字乱码 参看了 渔人码头[http://www.cnblo ...

  8. ubuntu 11.10下载和编译Android源码

    ubuntu 11.10下载和编译Android源码 由衷感谢网上的各个博主的文章,给了小弟我莫大的帮助.这里呢,小弟也把自己的经历记录下来,以供大家参考^^.这篇应该算是零基础.从无到有的流程介绍了 ...

  9. java 1.6.0.11_ubuntu 11.10安装java1.6(转)

    声明:一下内容主要来自网络,本来在此基础上根据自己的成功经验做了一些浅陋的(但有价值的)备注说明和修改. (本人也是ubuntu 11.10版本,但是是64位的,因此安装的java也是64位版本.) ...

最新文章

  1. bo a1 蓝牙音箱 中文说明
  2. 看一遍那些网络上晦涩的名词都弄明白了!
  3. python常用编译器和解释器的区别_Python常用编译器原理及特点解析
  4. SVM支持向量机(下)
  5. 【大话数据结构算法】哈夫曼树
  6. mac11.5.2版本虚拟机SeaBIOS不引导,kvm虚拟机状态为pause
  7. phpstudy mysql端口_完美解决phpstudy安装后mysql无法启动(无需删除原数据库,无需更改任何配置,无需更改端口)直接共存...
  8. centos6.4下安装jdk
  9. spring + hibernate + mysql 事务不回滚
  10. linux重启python服务_如何将python脚本作为linux服务启动
  11. 上海电力学院计算机组成与结构试卷,上海电力学院试卷及成绩管理办法
  12. lucene使用与优化
  13. 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之64---BREW 应用的交叉编译...
  14. E001检测到您的环境不支持HTML5,艾默生变频器常见故障及解决方法
  15. 三极管的基础知识(下)①
  16. 程序员到底是吃青春饭还要奋斗?那么行业前景又如何?
  17. anchor和正负样本
  18. IDEA创建项目时弹出链接超时的提示,亲测好用的解决办法
  19. vue2中vant实现网易云音乐案例-附带所有源码
  20. 比肩某应的顶级AI电销机器人无限部署

热门文章

  1. mega2560电脑识别不到端口后_Qt音视频开发41-人脸识别嵌入式
  2. mysql view 子查询_mysql – View的SELECT包含FROM子句中的子查询
  3. python内置函数map_Python内置函数(34)——map
  4. 期刊投稿状态_论文投稿,你不知道的那些事
  5. python字典popitem_Python 字典(Dictionary) | 菜鸟教程
  6. 手机支付优惠促销活动插画素材,拿来就能用,高效省时。
  7. html质感阴影_CSS3 阴影(text-shadow)
  8. c++网络编程连接成功后回调onconnected_谈谈网络编程(基于C++)
  9. TCP/IP网络协议栈:以太网数据包结构、802.3、MTU
  10. 关于布隆过滤器的所有信息:利用Hash实现的索引方案