求助mfc中已mfc定义全局变量类的解析多义性问题

MFC中的数据类型_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
MFC中的数据类型
上传于||文档简介
&&介​绍​了​M​F​C​中​常​见​的​数​据​类​型
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
定制HR最喜欢的简历
你可能喜欢本帖子已过去太久远了,不再提供回复功能。VC6.0 MFC中:POSITION结构体解析
VC的基本头文件AFX.h里对POSITION是这样定义的: struct __POSITION { }; typedef __POSITION* POSITION 显然这里定义了一个没有任何字段的结构体,而POSITION是这个结构体的指针。 VC在使用CList时,一些函数往往用POSITION参数,或者返回POSITION类型,其实查看CList的原代码就知道,原来CList返回的POSITION就是它的链表的一个结点指针,也就是说,在CList里有一个CNode的类定义了结点,而POSITION就是CNode* 所以说,虽然__POSITION结构体没有任何字段,但用POSITION结构体指针,仅仅用来表达CNode*指针,并且是强制转换过来的。于是POSITION被VC用为一种常见的数据类型---32位指针。
下面是CList中它的用法&AFX_INLINE & TYPE& & CList &TYPE, & ARG_TYPE& ::GetNext(POSITION& & rPosition) & // &&{ &&& CNode* & pNode & = & (CNode*) & rP&& ASSERT(AfxIsValidAddress(pNode, & sizeof(CNode)));&& rPosition & = & (POSITION) & pNode-& pN&& return & pNode-&&}&
试了试空结构,很奇怪&struct & SS{};&SS & s={0}; & & 会分配4字节内存做s变量,但并不初始化这4字节为0&int & b=sizeof(SS); & 为1&
在MFC4.0以前的版本中,POSITION实际上就是一个void指针。在5.0中的定义大概是:&struct & __POSITION & { & int & & } & ;&typedef & __POSITION & * & POSITION & ;&6.0中才变为:&struct & __POSITION & { & & } & ;&typedef & __POSITION & * & POSITION & ;&虽然结构内没有成员变量,但指向结构的指针仍需要4字节----32位机中天下的指针都是这个长度滴!&这样做的目的仅是为了类型安全。对于POSITION参数,使用者很容易传错,特别是当表中元素是void & *时,此时很有可能在需要元素的地方传入POSITION,而在需要POSITION的地方传入void & *。当做上面的定义中,编译器就可以检查出这种错误。&在MFC内部,POSITION实际上指向CNode类型
请各位遵纪守法并注意语言文明& MFC六大核心机制之一:MFC程序的初始化
MFC六大核心机制之一:MFC程序的初始化
&&&&&& 很多做软件开发的人都有一种对事情刨根问底的精神,例如我们一直在用的,很方便,不用学太多原理性的知识就可以做出各种窗口程序,但喜欢钻研的朋友肯定想知道,到底微软帮我们做了些什么,让我们在它的框架下可以简单的写程序。本文开始就跟大家分享一位同行前辈写的MFC核心机制分析(稍作整理),语言朴实易懂,在读完此深入浅析的剖析系列后,相信留给大家的是对MFC运行机制的深入理解。&&&&&& MFC六大核心机制概述&&&&&& 我们选择了,主要是因为它够艺术、够自由,使用它我们可以实现各种想法,而MFC将多种可灵活使用的功能封装起来,我们岂能忍受这种&黑盒&操作?于是研究分析MFC的核心机制成为必然。&& &&& 首先,列出要讲的MFC六大核心机制:&& &&& 1、MFC程序的初始化。&& &&& 2、运行时类型识别(RTTI)。&& &&& 3、动态创建。&& &&& 4、永久保存。&& &&& 5、消息映射。&&& && 6、消息传递。&&&&&& 本文讲第一部分,MFC程序的初始化过程。&&&&&& 简单的MFC窗口程序&&&&&& 设计一个简单完整MFC程序,产生一个窗口。当然这不能让AppWizard自动为我们生成。我们可以在Win32 Application工程下面那样写:
#include&&afxwin.h& &&
class&MyApp&:&public&CWinApp &&
public: &&
BOOL&InitInstance()&&&&
&&CFrameWnd&*Frame=new&CFrameWnd();&&
&&m_pMainWnd=F&&&
&&Frame-&Create(NULL,&最简单的窗口&);&&
&&Frame-&ShowWindow(SW_SHOW);&&&&
&&return&true;&&&&&&&&&&&
MyApp&theA&&&&&&&&&& 设定链接MFC库,运行,即可看见一个窗口。&&&&&& 从上面,大家可以看到建立一个MFC窗口很容易,只用两步:一是从CWinApp派生一个应用程序类(这里是MyApp),然后建立应用程序对象(theApp),就可以产生一个自己需要的窗口(即需要什么样就在InitInstance()里创建就行了)。&&&&&& 整个程序,就改写一个InitInstance()函数,创建那么一个对象(theApp),就是一个完整的窗口程序。这就是&黑盒&操作的魔力!&&&&&& 在我们正想为微软鼓掌的时候,我们突然觉得心里空荡荡的,我们想知道微软帮我们做了什么事情,而我们想编自己的程序时又需要做什么事情,哪怕在上面几行的程序里面,我们还有不清楚的地方,比如,干嘛有一个m_pMainWnd指针变量,它从哪里来,又要到哪里去呢?想一想在DOS下编程是多么美妙的一件事呵,我们需要什么变量,就声明什么变量,需要什么样的函数,就编写什么样的函数,或者引用函数库&&但是现在我们怎么办?&&&&&& 我们可以逆向思维一下,MFC要达到这种效果,它是怎么做的呢?首先我们要弄明白,不是一种语言,它就象我们学c语言的时候的一个类似记事本的编辑器(请原谅我的不贴切的比喻),所以,在VC里面我们用的是C++语言编程,C++才是根本(初学者总是以为VC是一门什么新的什么语言,一门比C++先进很多的复杂语言,汗)。说了那么多,我想用一句简单的话概括&MFC黑箱&,就是为我们的程序加入一些固化的&C++代码&的东西&。&&&&&& 既然MFC黑箱帮我们加入了代码,那么大家想想它会帮我们加入什么样的代码呢?他会帮我们加入求解一元二次方程的代码吗?当然不会,所以它加入的实际上是每次编写窗口程序必须的,通用的代码。&&&&&& 再往下想,什么才是通用的呢?我们每次视窗编程都要写WinMain()函数,都要有注册窗口,产生窗口,消息循环,回调函数&&即然每次都要的东西,就让它们从我们眼前消失,让MFC帮忙写入!&&&&&& 手动模拟MFC程序的初始化&&&&&&&&&&&& 要知道MFC初始化过程,大家当然可以跟踪执行程序。但这种跟踪很麻烦,我相信大家都会跟踪的晕头转向。本人觉得哪怕你理解了MFC代码,也很容易让人找不着北,我们完全不懂的时候,在成千上万行程序的迷宫中如何能找到出口?&&&&&& 我们要换一种方法,不如就来重新编写个MFC库吧,哗!大家不要笑,小心你的大牙,我不是疯子(虽然疯子也说自己不疯)。我们要写的就是最简单的MFC类库,就是把MFC宏观上的,理论上的东西写出来。我们要用最简化的代码,简化到刚好能运行。&&&&&& 1、需要&重写&的MFC库&&&&&&&既然,我们这一节写的是MFC程序的初始化过程,上面我们还有了一个可执行的MFC程序。程序中只是用了两个MFC类,一个是CWinApp,另一个是CFrameWnd。当然,还有很多同样重要MFC类如视图类,文档类等等。但在上面的程序可以不用到,所以暂时省去了它(总之是为了简单)。&&&&&& 好,现在开始写MFC类库吧&&唉,面前又有一个大难题,就是让大家背一下MFC层次结构图。天,那张鱼网怎么记得住,但既然我们要理解他,总得知道它是从那里派生出来的吧。&&&&&& 考虑到大家都很辛苦,那我们看一下上面两个类的父子关系(箭头代表派生):&&&&&&&CObject-&CCmdTarget-&CWinThread-&CWinApp-&自己的重写了InitInstance()的应用程序类。&&&&&& CObject(同上)-&CCmdTarget(同上)-&CWnd-&CFrameWnd&&&&&& 看到层次关系图之后,终于可以开始写MFC类库了。按照上面层次结构,我们可以写以下六个类(为了直观,省去了构造函数和析构函数)。
class&CObiect{};&&
class&CCmdTarget&:&public&CObject{}; &&
------------------------------------------------ &&
class&CWinThread&:&public&CCmdTarget{}; &&
class&CWinApp&:&public&CWinThread{}; &&
------------------------------------------------ &&
class&CWnd&:&public&CCmdTarget{}; &&
class&CFrameWnd&:&public&CWnd{}; &&
&&&&&&&& 大家再想一下,在上面的类里面,应该有什么?大家马上会想到,CWinApp类或者它的基类CCmdTarget里面应该有一个虚函数virtual BOOL InitInstance(),是的,因为那里是程序的入口点,初始化程序的地方,那自然少不了的。可能有些朋友会说,反正InitInstance()在派生类中一定要重载,我不在CCmdTarget或CWinApp类里定义,留待CWinApp的派生类去增加这个函数可不可以。扯到这个问题可能有点越说越远,但我想信C++的朋友对虚函数应该是没有太多的问题的。总的来说,作为程序员如果清楚知道基类的某个函数要被派生类用到,那定义为虚函数要方便很多。&&&&&& 也有很多朋友问,C++为什么不自动把基类的所有函数定义为虚函数呢,这样可以省了很多麻烦,这样所有函数都遵照派生类有定义的函数就调用派生类的,没定义的就调用基类的,不用写virtual的麻烦多好!其实,很多面向对象的语言都这样做了。但定义一个虚函数要生成一个虚函数表,要占用系统空间,虚函数越多,表就越大,有时得不偿失!这里哆嗦几句,是因为往后要说明的消息映射中大家更加会体验到这一点,好了,就此打往。&&&&&& 上面我们自己解决了一个问题,就是在CCmdTarge写一个virtual BOOL InitInstance()。&&&&&& 2、WinMain()函数和CWinApp类&&&&&& 大家再往下想,我们还要我们MFC&隐藏&更多的东西:WinMain()函数,设计窗口类,窗口注册,消息循环,回调函数&&我们马上想到封装想封装他们。大家似乎隐约地感觉到封装WinMain()不容易,觉得WinMain()是一个特殊的函数,许多时候它代表了一个程序的起始和终结。所以在以前写程序的时候,我们写程序习惯从WinMain()的左大括写起,到右大括弧返回、结束程序。&&&&&& 我们换一个角度去想,有什么东西可以拿到WinMain()外面去做,许多初学者们,总觉得WinMain()函数是天大的函数,什么函数都好象要在它里面才能真正运行。其实这样了解很片面,甚至错误。我们可以写一个这样的C++程序:
#include&&iostream.h& &&
class&test{ &&
public: &&
&test(){cout&&&请改变你对main()函数的看法!&&&} &&
test&test1; &&
void&main(){} &&
&&&&&&&& 在上面的程序里,入口的main()函数表面上什么也不做,但程序执行了(注:实际入口函数做了一些我们可以不了解的事情),并输出了一句话(注:全局对象比main()首先运行)。现在大家可以知道我们的WinMain()函数可以什么都不做,程序依然可以运行,但没有这个入口函数程序会报错。&&&&&& 那么WinMain()函数会放哪个类上面呢,请看下面程序:
#include&&afxwin.h& &&
class&MyApp&:&public&CWinApp &&
public: &&
&BOOL&InitInstance()&&&&
&&AfxMessageBox(&程序依然可以运行!&); &&
&&return&true; &&
MyApp&theA&&&&&&&&&& 大家可以看到,我并没有构造框架,而程序却可以运行了&&弹出一个对话框(如果没有WinMain()函数程序会报错)。上面我这样写还是为了直观起见,其实我们只要写两行程序:&&&&&& #include &afxwin.h&&&&&&& CWinApp theA&&&& //整个程序只构造一个CWinApp类对象,程序就可以运行!&&&&&& 所以说,只要我们构造了CWinApp对象,就可以执行WinMain()函数。我们马上相信WinMain()函数是在CWinApp类或它的基类中,而不是在其他类中。其实这种看法是错误的,我们知道编写C++程序的时候,不可能让你在一个类中包含入口函数,WinMain()是由系统调用,跟我们的平时程序自身调用的函数有着本质的区别。我们可以暂时简单想象成,当CWinApp对象构造完的时候,WinMain()跟着执行。&&&&&& 现在大家明白了,大部分的&通用代码(我们想封装隐藏的东西)&都可以放到CWinApp类中,那么它又是怎样运行起来的呢?为什么构造了CWinApp类对象就&自动&执行那么多东西。&&&&&& 大家再仔细想一下,CWinApp类对象构造之后,它会&自动&执行自己的构造函数。那么我们可以把想要&自动&执行的代码放到CWinApp类的构造函数中。&&&&&& 那么CWinApp类可能打算这样设计(先不计较正确与否):
class&CWinApp&:&public&CWinThead{ &&
public: &&
virtual&BOOL&InitInstance();&&&
&&CWinApp&::CWinApp(){&&&&&
&&&WinMain();&&&&&
&&&Create();&&&&&&
&&&Run();&&&&&&&
};&&&&&&&&&写完后,大家又马上感觉到似乎不对,WinMain()函数在这里好象真的一点用处都没有,并且能这样被调用吗(请允许我把手按在圣经上声明一下:WinMain()不是普通的函数,它要肩负着初始化应用程序,包括全局变量的初始化,是由系统而不是程序本身调用的,WinMain()返回之后,程序就结束了,进程撤消)。再看Create()函数,它能确定设计什么样的窗口,创建什么样的窗口吗?如果能在CWinApp的构造函数里确定的话,我们以后设计MFC程序时窗口就一个样,这样似乎不太合理。&&&&&&&回过头来,我们可以让WinMain()函数一条语句都不包含吗?不可以,我们看一下WinMain() 函数的四个参数:&&&&&& WinMain(HINSTANCE, HINSTANCE, LPSTR, int)&&&&&& 其中第一个参数指向一个实例句柄,我们在设计WNDCLASS的时候一定要指定实例句柄。我们窗口编程,肯定要设计窗口类。所以,WinMain()再简单也要这样写:&&&&&& int WinMain(HINSTANCE hinst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)&&&&&& {& hInstance=hinst }&&&&&& 既然实例句柄要等到程序开始执行才能知道,那么我们用于创建窗口的Create()函数也要在WinMain()内部才能执行(因为如果等到WinMain()执行完毕后,程序结束,进程撤消,当然Create()也不可能创建窗口)。&&&&&& 再看Run()(消息循环)函数,它能在WinMain()函数外面运行吗?众所周知,消息循环就是相同的那么几句代码,但我们也不要企图把它放在WinMain()函数之外执行。&&&&&& 所以我们的WinMain()函数可以像下面这样写:&&&&&& WinMain(&&)&&&&&& {&&&&&&&&&&&&& &&窗口类对象执行创建窗口函数&&&&&&&&&&&&&&& &&程序类对象执行消息循环函数&&&&&&&& }&&&&&& 对于WinMain()的问题,得总结一下,我们封装的时候是不可以把它封装到CWinApp类里面,但由于WinMain()的不变性(或者说有规律可循),MFC完全有能力在我们构造CWinApp类对象的时候,帮我们完成那几行代码。&&&&&& 转了一个大圈,我们仿佛又回到了SDK编程的开始。但现在我们现在能清楚地知道,表面上MFC与SDK编程截然不同,但实质上MFC只是用类的形式封装了SDK函数,封装之后,我们在WinMain()函数中只需要几行代码,就可以完成一个窗口程序。我们也由此知道了应如何去封装应用程序类(CWinApp)和主框架窗口类(CFrameWnd)。下面把上开始设计这两个类。&&&&&& 3、MFC库的&重写&&&&&&& 为了简单起见,我们忽略这两个类的基类和派生类的编写,可能大家会认为这是一种很不负责任的做法,但本人觉得这既可减轻负担,又免了大家在各类之间穿来穿去,更好理解一些(我们在关键的地方作注明)。还有,我把全部代码写在同一个文件中,让大家看起来不用那么吃力,但这是最不提倡的写代码方法,大家不要学哦!
#include&&windows.h& &&
HINSTANCE&hI &&
class&CFrameWnd&& &&
public: &&
&CFrameWnd();&&&&&
&virtual&~CFrameWnd(); &&
&int&Create();&&&&&&
&BOOL&ShowWnd(); &&
class&CWinApp1&& &&
public: &&
&CFrameWnd*&m_pMainW&&
&CWinApp1*&m_pCurrentWinA&&
&CWinApp1(); &&
&virtual&~CWinApp1(); &&
&virtual&BOOL&InitInstance();&&
&virtual&BOOL&Run();&&
CFrameWnd::CFrameWnd(){} &&
CFrameWnd::~CFrameWnd(){} &&
int&CFrameWnd::Create()&&&&&
&WNDCLASS& &&
&wndcls.style=0; &&
&wndcls.cbClsExtra=0; &&
&wndcls.cbWndExtra=0; &&
&wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); &&
&wndcls.hCursor=LoadCursor(NULL,IDC_CROSS); &&
&wndcls.hIcon=LoadIcon(NULL,IDC_ARROW); &&
&wndcls.hInstance=hI &&
&wndcls.lpfnWndProc=DefWindowP&&
&wndcls.lpszClassName=&窗口类名&; &&
&wndcls.lpszMenuName=NULL; &&
&RegisterClass(&wndcls); &&
&hwnd=CreateWindow(&窗口类名&,&窗口实例标题名&,WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL); &&
&&return&0; &&
BOOL&CFrameWnd::ShowWnd()&&
&ShowWindow(hwnd,SW_SHOWNORMAL); &&
&UpdateWindow(hwnd); &&
&return&0; &&
CWinApp1::CWinApp1() &&
&m_pCurrentWinApp=this; &&
CWinApp1::~CWinApp1(){} &&
BOOL&CWinApp1::InitInstance() &&
&m_pMainWnd=new&CFrameW &&
&m_pMainWnd-&Create(); &&
&m_pMainWnd-&ShowWnd(); &&
&return&0; &&
BOOL&CWinApp1::Run()&&
&while(GetMessage(&msg,NULL,0,0)) &&
&&TranslateMessage(&msg); &&
&&DispatchMessage(&msg); &&
&return&0; &&
CWinApp1&theA&&&&&
int&WINAPI&WinMain(&HINSTANCE&hinst,&HINSTANCE&hPrevInstance,&&&LPSTR&lpCmdLine,&&int&nCmdShow) &&
&hInstance= &&
&CWinApp1*&pApp=theApp.m_pCurrentWinA &&
&pApp-&InitInstance(); &&
&pApp-&Run(); &&
&return&0; &&
}&&&&&&&& 代码那么长,实际上只是写了三个函数,一是CFrameWnd类的Create(),第二个是CWinApp类的InitInstance()和Run()。在此特别要说明的是InitInstance(),真正的MFC中,那是我们跟据自己构造窗口的需要,自己改写这个函数。&&&&&& 大家可以看到,封装了上面两个类以后,在入口函数WinMain中就写几行代码,就可以产生一个窗口程序。在MFC中,因为WinMain函数就是固定的那么几行代码,所以MFC绝对可以帮我们自动完成(MFC的特长就是帮我们完成有规律的代码),也因此我们创建MFC应用程序的时候,看不到WinMain函数。分享源自:http://blog.csdn.net/liyi268/article/details/297875
除非特别注明,文章均为原创
转载请标明本文地址:
作者:鸡啄米
&&( 22:56:16)&&( 22:4:12)&&( 21:56:47)&&( 21:53:16)&&( 22:0:34)&&( 22:19:2)&&( 22:53:50)&&( 21:22:28)&&( 22:41:48)&&( 22:53:35)
路过,过来看看。欢迎不?鸡啄米 于
23:20:06 回复必须欢迎啊
学到了不少知识,感谢博主的分享~
楼主好厉害啊,学习下鸡啄米 于
22:41:32 回复过奖了,常来啊
专业博客 缔造专业鸡啄米 于
22:42:09 回复言简意赅
技术牛人!
请问如何链接MFC库?谢谢
我复制了后面的代码,为什么运行不了,显示有错误。作者能否详细说下,如果要运行该程序,还需要哪些设置?
analogMFC_2.obj : error LNK2019: 无法解析的外部符号 __imp__CreateWindowExA@48,该符号在函数 &public: int __thiscall CFrameWnd::Create(void)& (?Create@CFrameWnd@@QAEHXZ) 中被引用1&analogMFC_2.obj : error LNK2019: 无法解析的外部符号 __imp__RegisterClassA@4,该符号在函数 &public: int __thiscall CFrameWnd::Create(void)& (?Create@CFrameWnd@@QAEHXZ) 中被引用1&analogMFC_2.obj : error LNK2019: 无法解析的外部符号 __imp__DefWindowProcA@16,该符号在函数 &public: int __thiscall CFrameWnd::Create(void)& (?Create@CFrameWnd@@QAEHXZ) 中被引用1&analogMFC_2.obj : error LNK2019: 无法解析的外部符号 __imp__LoadIconA@8,该符号在函数 &public: int __thiscall CFrameWnd::Create(void)& (?Create@CFrameWnd@@QAEHXZ) 中被引用1&analogMFC_2.obj : error LNK2019: 无法解析的外部符号 __imp__LoadCursorA@8,该符号在函数 &public: int __thiscall CFrameWnd::Create(void)& (?Create@CFrameWnd@@QAEHXZ) 中被引用显示如下错误!
后面的代码不要链接MFC库就可以用了。感谢博主的分享!
&我们岂能忍受这种“黑盒”操作?&
说的好,开发人员就应该具有这样的想法,刨根问底。
为什么我输入那个空的main函数编译,提示无法启动程序?#include &iostream&class test{public: test() {
cout && &请改变你对main()函数的看法!& && }};test test1;/*************************/void main(){}//////////////
完全随机文章& 今天早上我在调试MFC程序的时候碰到了一个非常棘手的问题,一开始程序还是好的,可以正常运行,一会突然就不能运行,提示到如下的错误:
软件下面的错误提示窗口显示有2个错误:
刚开始自己也看不懂这个错误是什么意思,于是直接在网上找解决办法,子啊百度里找到了一些解决办法,一个个试了,都不行!好恼火。。。。于是静下心来看这个错误。照着错误说明找到了XXXDLG.h的头文件,发现在这个头文件里OnEnChangeEditReceive();有错误,下面画的绿色波浪线(因为.cpp文件里没有提示错误的红色波浪线),于是将此错误定位在这个.h的头文件里。
最好使用高版本的Visual Stdio编程软件,我使用的是VS2015。错误行最左边有一个黄色的灯泡,点击黄色电灯泡上的下拉菜单,选择第三个“创建声明/定义(C)”的一项,然后会自动在你的.cpp文件后面声明该函数OnEnChangeEditReceive(),如下图所示:
之后在.cpp文件出现如下所示的几行程序,点击下图右上角的小×,则函数声明成功,报错消失。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:11次
排名:千里之外

我要回帖

更多关于 mfc自定义控件开发 的文章

 

随机推荐