- 浏览: 2049446 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (795)
- java (263)
- 聚类搜索引擎 (9)
- 经验之谈 (67)
- DSP (3)
- C++ (140)
- Linux (37)
- SNMP (6)
- Python (6)
- 数据库 (61)
- 网络 (20)
- 算法 (15)
- 设计模式 (4)
- 笔试题 (38)
- 散文 (35)
- 数据结构 (9)
- 银行知识 (0)
- 榜样 (9)
- Lucene (15)
- Heritrix (6)
- MetaSeeker (0)
- netbeans (12)
- php (3)
- 英语 (8)
- DB2 (0)
- java基础 (5)
- mongodb & hadoop (4)
- Javascript (7)
- Spring (4)
- ibatis & myibatis (1)
- velocity (1)
- 微服务 (0)
- paddle (1)
- 第三方 (0)
- 知识沉淀 (1)
- 建模 (0)
最新评论
-
0372:
标示对java很陌生!
中文乱码解决的4种方式 -
梦留心痕:
Java中\是转意字符, 可是你的这句话我没看懂,只要把得到的 ...
java中如何忽略字符串中的转义字符--转载 -
yanjianpengit:
[b][/b]
java为什么非静态内部类里面不能有静态成员 -
springdata-jpa:
可以参考最新的文档:如何在eclipse jee中检出项目并转 ...
eclipse 如何把java项目转成web项目 -
qq1130127172:
,非常好。
(转)SpringMVC 基于注解的Controller @RequestMapping @RequestParam..
Visual C++ 6.0 以其功能强大、用户界面友好而倍受程序员们的青睐。但是,在当前的Microsoft 基本类库4.2 版本中,大约有将近
200 个类,数千个函数,加之Microsoft 公司隐藏了一些技术细节,使得人们深入学习MFC变得十分困难。
MFC的AppWizard可以生成三种类型的应用程序:基于对话框的应用、单文档应用(SDI)和多文档应用(MDI)。前两者的结构较简单,本文不再赘叙。笔者拟从MFC中的文档/视结构入手,分析一些函数的流程,并解决编制MDI 应用程序过程中的一些常见问题。
(一)、了解文档/视结构
MFC应用程序模型历经多年以有了相当大的发展。有一个时期,它只是个使用应用程序对象和主窗口对象的简单模型。在这个模型中,应用程序的数据作为成员变量保持在框架窗口类中,在框架窗口的客户区中,该数据被提交显示器。随着MFC2。0的问世,一种应用程序结构的新方式----MFC文档/视结构出现了。在这种结构中,CFrameWnd繁重的任务被委派给几个不同类,实现了数据存储和显示的分离。一般情况下,采用文档/视结构的应用程序至少应由以下对象组成:
。应用程序是一个CwinApp派生对象,它充当全部应用程序的容器。应用程序沿消息映射网络分配消息给它的所有子程序。
。框架窗口是一CfrmeWnd派生对象。
。文档是一个CDocument派生对象,它存储应用程序的数据,并把这些信息提供给应用程序的其余部分。
。视窗是Cview派生对象,它与其父框架窗口用户区对齐。视窗接受用户对应用程序的输入并显示相关联的文档数据。
通常,应用程序数据存在于简单模型中的框架窗口中。在文档/视方式中,该数据移入称为document的独立数据对象。当然,文档不一定是文字,文档是可以表现应用程序使用的数据集的抽象术语。而用户输入处理及图形输出功能从框架窗口转向视图。单独的视窗完全遮蔽框架窗口的客户区,这意味着即使程序员直接绘画至框架窗口的客户区,视图仍遮蔽绘画,在屏幕上不出现任何信息。所以输出必须通过视图。框架窗口仅仅是个视图容器。
CDocument类对文档的建立及归档提供支持并提供应用程序用于控制其数据的接口。MDI应用程序可以处理多个类型的文档,每个类型的文档拥有一个相关联的文档模板对象。文档对象驻留在场景后面,提供由视图对象显示的信息。文档至少有一个相关联的视图。视图只能与一个文档相关联。
在文档/视方式中,对象的建立是由文档模板来管理的,它是CDocTemplate派生对象,建立并维护框架窗口,文档及视。
MFC调用命令处理程序以响应发生在应用程序中的事件。命令发送的优先级是:
活动的视图->框架窗口->文档->应用程序->默认窗口过程(DefWindowsProc)
总之,在文档/视方式中,文档和视是分离的,即:文档用于保存数据,而视是用来显示这些数据。文档模板维护它们之间的关西。这种文档/视结构在开发大型软件项目时特别有用。
(二)、了解与文档/视结构有关的各种类之间的关系。
在文档/视应用程序中,CWinApp对象拥有并控制文档模板,后者产生文档、框架窗口及视窗。
从用户的角度来看,“视”实际上是一个普通的窗口。象其他基于Widnows应用的窗口一样,人们可以改变它的尺寸,对它进行移动,也可以随时关闭它。若从程序员的角度来看,视实际上是一个从MFC类库中的Cview类所派生出的类的对象。文档对象是用来保存数据的,而视对象是用来显示数据的,并且允许对数据进行编辑。SDI或MDI的文档类是由Cdocument类派生出来的,它可以有一个或多个视类,而这些视类最终都是由Cview类派生出来的。视对象只有一个与之相联系的文档对象,它所包含的CView::GetDocument函数允许应用在视中得到与之相联系的文档,据此,应用程序可以对文档类成员函数及公共数据成员进行访问。如果视对象接受到了一条消息,表示用户在编辑控制中输入了新的数据,此时,视就必须通知文档对象对其内部数据进行相应的更新。
如果文档数据发生了变化,则所有的视都必须被通知到,以便它们能够对所显示的数据进行相应的更新。Cdocument::UpdateAllViews函数即可完成此功能。当该函数被调用时,派生视类的CView::OnUpdate函数被触发。通常OnUpdate函数要对文档进行访问,读取文档数据,然后再对视的数据成员或控制进行更新,以便反映出文档的变化。另外,还可以利用OnUpdate函数使视的部分客户区无效,以便触发Cview::OnDraw函数,利用文档数据来重新对窗口进行绘制。
在MDI应用程序中,可以处理多个文档类型,即多个文档模板,每个模板又可以有多个文档,每个文档又可以多视显示。为管理方便,上一级往往保留了下一级的指针列表。解释如下:
(1)、每个应用程序类(CwinApp的派生类)都保留并维护了一份所有文档模板的指针列表,这是一个链表结构。应用程序为所要支持的每个文档类型动态分配一个CMultiDocTemplate 对象,
CmultiDocTemplate(UINT nIDResource,
CruntimeClass * pDocClass,
CruntimeClass * pFrameClass,
CruntimeClass * pViewClass );
并在应用程序类的CWinApp::InitInstance成员函数中将每个CMultiDocTemplate对象传递给CWinApp::AddDocTemplate。 该函数将一个文档模板加入到应用程序可用文档模板的列表中。函数原形为:
void AddDocTemplate(CdocTemplate * pTemplate);
应用程序可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板的位置,利用该值来调用CWinApp::GetNextDocTemplate函数,获得第一个CDocTemplate对象指针。函数原形如下:
POSITION GetFirstDocTemplate( ) const;
CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;
第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象指针检索的值。通过这两个函数,应用程序可以遍历整个文档模板列表。如果被检索的文档模板是模板列表中的最后一个,则pos参数被置为NULL。
(2)、一个文档模板可以有多个文档,每个文档模板都保留并维护了一个所有对应文档的指针列表。应用程序可以用CDocTemplate::GetFirstDocPosition函数获得与文档模板相关的文档集合中第一个文档的位置,并用POSITION值作为CDocTemplate::GetNextDoc的参数来重复遍历与模板相关的文档列表。函数原形为:
viaual POSITION GetFirstDocPosition( ) const = 0;
visual Cdocument *GetNextDoc(POSITION & rPos) const = 0;
如果列表为空,则rPos被置为NULL.
(3)、在文档中可以调用CDocument::GetDocTemplate获得指向该文档模板的指针。函数原形如下:
CDocTemplate * GetDocTemplate ( ) const;
如果该文档不属于文档模板管理,则返回值为NULL。
(4)、一个文档可以有多个视。每一个文档都保留并维护一个所有相关视的列表。CDocument::AddView将一个视连接到文档上,将该视加入到文档相联系的视的列表中,并将视的文档指针指向该文档。当有File/New、File/Open、Windows/New或Window/Split的命令而将一个新创建的视的对象连接到文档上时, MFC会自动调用该函数,框架通过文档/视的结构将文档和视联系起来。当然,程序员也可以根据自己的需要调用该函数。
Virtual POSITION GetFirstViewPosition( ) const;
Virtual CViw * GetNextView( POSITION &rPosition) cosnt;
应用程序可以调用CDocument::GetFirstViewPosition返回与调用文档相联系的视的列表中的第一个视的位置,并调用CDocument::GetNextView返回指定位置的视,并将rPositon的值置为列表中下一个视的POSITION值。如果找到的视为列表中的最后一个视,则将rPosition置为NULL.
当在文档上新增一个视或删除一个视时,MFC会调用OnChangeViewList函数。如果被删除的视是该文档的最后一个视,则删除该文档。
(5)、一个视只能有一个文档。在视中,调用CView::GetDocument可以获得一个指向视的文档的指针。函数原形如下:
CDocument *GetDocument ( ) const;
如果该视不与任何文档相,则返回NULL.
(6)、MDI框架窗口通过调用CFrameWnd::GetActiveDocument 可以获得与当前活动的视相连的CDocument 指针。函数原形如下:
virtual CDocument * GetActiveDocument( );
(7)、通过调用CFrameWnd::GetActiveView 可以获得指向与CFrameWnd框架窗口连接的活动视的指针,如果是被CMDIFrameWnd框架窗口调用,则返回NULL。MDI框架窗口可以首先调用MDIGetActive找到活动的MDI子窗口,然后找到该子窗口的活动视。函数原形如下:
virtual Cdocument * GetActiveDocument( );
(8)、MDI框架窗口通过调用CFrameWnd::GetActiveFrame, 可以获得一个指向MDI框架窗口的活动多文档界面子窗口的指针。
(9)、CMDIChildWnd调用GetMDIFrame获得MDI框架窗口(CMDIFrameWnd)。
(10)、CWinApp 调用AfxGetMainWnd得到指向应用程序的活动主窗口的指针。
下面一段代码,就是利用CDocTemplate、CDocument和CView之间的存取关系,遍历整个文档模板、文档以及视。
CMyApp * pMyApp = (CMyApp *)AfxGetApp();
POSITION p = pMyApp->GetFirstDocTemplatePosition();
while(p!= NULL)
{
CDocTemplate * pDocTemplate = pMyApp->GetNextDocTemplate(p);
POSITION p1 = pDocTemplate->GetFirstDocPosition();
while(p1 != NULL)
{
CDocument * pDocument = pDocTemplate->GetNextDoc(p1);
POSITION p2 = pDocument->GetFirstViewPosition();
while(p2 != NULL)
{
CView * pView = pDocument->GetNextView(p2);
}
}
}
(图4)、遍历整个文档模板、文档和视
在应用程序的任何地方,程序员都可以调用AfxGetApp( )获得应用程序的对象指针。由于本文着重介绍文档/视的关系,至于框架窗口之间的关系没能列全,读者可以查相应的文档。
(三)、了解CwinApp::OnFileNew、CwinApp::OnFileOpen和Window/New的程序流程。
(1)、CwinApp::OnFileNew和CwinApp::OnFileOpen函数的简单流程。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
在CWinApp::OnFile/new 或CwinApp::OnFileOpen函数中,核心操作是CDocTemplate::OpenDocument函数。其函数原型为:
virtual CDocument* CDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
BOOL bMakeVisible =TRUE ) = 0;
图(4)中星号标注之后即是该函数的流程,简要介绍如下:
(1)、CDocTemplate::CreateNewDocument函数创建一个新文档,其类型与文档模板相关,并通过函数CDocTemplate::AddDocument加入该文档模板的文档指针列表中。此时,文档类的构造函数被执行,程序可以在此进行文档的初始化。
(2)、函数CDocTemplate::CreateNewFrame调用MDI子窗口类(CMDIChildWnd)的构造函数,生成MDI子窗口对象。接着调用CMDIChildWnd::PreCreateWindow。然后,生成一个CCreateContext对象,(CcreateContext是MFC框架所使用的一种结构,它将构成文档和视的组件联系起来。后文将详细介绍之。)并将该对象值传给CMDIChildWnd::OnCreateClient函数。MFC调用此函数,用CCreateContext对象提供的信息创建一个或多个CView对象。此时,各视的构造函数被依次调用。
(3)、接着,判断lpszPathName是否为空。分为两种情况:
(a)、若为空,则表明要创建一个新文档:调用SetDefaultTitle函数装载文档的缺省标题,并显示在文档的标题栏中;然后执行CDocument::OnNewDocument。该函数调用DeleteContents以保证文档为空,然后置新文档为清洁。可以重载该函数。
(b)、否则,表明要打开一个已存在的文档:调用CDocument::OnOpenDocument打开指定的文件;执行DeleteContext,保证文档为空;调用CObject::Serialize读入该文件的内容。(程序员可在此进行文件的读入操作。当然,也可以在CDocument::OnOpenDocument中读入文件)。然后置文档为清洁;最后,调用CDocTemplate::SetPathName,并把文件名加入到最近文件列表中。
(4)、调用CDocTemplate::InitialUpdateFrame函数,使框架窗口中的各个视收到OnInitialUpdate调用。框架窗口的主视(子窗ID等于AFX_IDW_PANE_FIRST的视)被激活。程序员可以在此对视对象进行初始化。
(2)、Window/New命令的程序流程
当主框架窗口上有子窗口时,选择Window/New命令可以生成该活动子窗口的影象。它们有相同的文档模板、相同的文档。其流程如下:
执行Window/New的过程与File/New的过程差不多。所不同的是,File/New须要创建一个新文档,而Window/New则是获得已存在的MDI子窗口的文档。因此以前存在的视和New以后生成的视均为该文档的视,都是该文档的内容的显示。当调用CDocument::UpdateAllViews函数时,它们(视)的OnUpdate函数都将被激活。此时,在该文档的视指针列表中,将有多于一个的视(具体数目视Window/New执行的次数而定)。读者可以利用(图3)中的代码跟踪程序结果。
(四)、几种情况的讨论
上面,笔者就MFC中文档/视的关系进行了分析,下面,笔者将结合具体情况进行讨论:
(1)、如何根据自己的要求来选择文档模板,及相应的视和文档。
在通常的MDI应用程序中,只有一个文档模板,程序员只能打开一种类型的文档。因此,程序员只要调用File/New或者File/Open创建或者打开文档即可,至于文档、视和框架窗口之间的关系,由文档模板在幕后控制,不须要对文档模板进行操作。但是,如果应用程序需要处理多种类型的文档,并且何时打开何种文档均需程序员手工控制,此时,程序员必须对文档模板进行编程。
例如,笔者需要处理AVI和BMP两种文件类型。AVI和BMP的数据存放格式不同,不能用同一的数据结构来描述,因此,把它们的数据都存入一个文档是不合适的。同时,由于AVI是图象序列,BMP仅是一幅图象,它们的显示是肯定不一样的,即它门的视不同。基于此,笔者决定分别建立两套文档模板,两套框架窗口,两套文档和两套视,分别用于AVI和BMP的数据存放和显示。程序可以根据用户选择的文件名来分别处理AVI和BMP。具体步骤如下:
(Step 1)、在应用程序类(CWinApp)的派生类中增加文档模板成员变量,以便对文档模板进行操作。
class C3dlcsApp : public CWinApp
{
......
public:
CMultiDocTemplate * m_pAVIDocTemplate;
CMultiDocTemplate * m_pBMPDocTemplate;
}
(Step 2)、在主框架中增加菜单响应:
void CMainFrame::OnFileOpen()
{
CFileDialog my(true);
if(my.DoModal()==IDOK)
{
CString FileName = my.GetPathName();
CString FileExt = my.GetFileExt();
if((FileExt == AVI) || (FileExt == avi))
{
CMyApp * pMyApp = (CMyApp *)AfxGetApp();
CMultiDocTemplate*pAVIDocTemplate=pMyApp->m_pAVIDocTemplate;
pAVIDocTemplate->OpenDocumentFile(FileName);
}
else if((FileExt == BMP) || (FileExt == bmp))
{
CMyApp * p3dlcsApp = (CMyApp *)AfxGetApp();
CMultiDocTemplate* pDATDocTemplate=pMyApp->m_pBMPDocTemplate;
pDATDocTemplate->OpenDocumentFile(FileName);
}
else
{
AfxMessageBox(Yor select a file not supported!);
return;
}
}
}
笔者把用户输入文件名的后缀作为分支条件,如果是AVI文件,则先获得关于AVI文件的文档模板,然后调用CDocTemplate::OpenUpdateFrame (lpszFileName)函数打开此文档。正如前面所分析,此函数将依次生成新文档,新框架,在CMDIChildWnd::OnCreateClient中创建视,最后向框架中所有的视发送初始化消息,使其显示在屏幕上。如果是BMP文件,操作类似。
当然,程序员也可以在程序的任何位置实现此操作:通过全局函数AfxGetApp 获得应用程序对象指针,从而获得相应的文档模板指针。
由于由AppWizard生成的应用程序会缺省调用CWinApp::OnFileNew,所以当程序开始执行时,会在主框架上显示一个新的空窗口。如果想去掉这个空窗口,只须重载CWinApp::OnFileNew函数,不许要任何代码,即可。
(2)、切分窗口与文档/视结构
一个文档可以有多个视,切分窗口即是表示多视的一种方法。 切分窗口是通过类CSplitterWnd来表示的,对Window来说,CSplitterWnd对象是一个真正的窗口,它完全占据了框架窗口的客户区域,而视窗口则占据了切分窗口的窗片区域。切分窗口并不参与命令传递机制,(窗片中)活动的视窗从逻辑上来看直接被连到了它的框架窗口中。
切分窗口可以分为动态和静态两种。前者较简单,本文仅讨论后者。创建切分窗口的步骤如下:
(Step 1)、在自己的框架窗口中声明成员变量,用以对切分窗口进行操作。
class CMyFrame : public CMDIChildWnd
{
......
CSplitterWnd m_Splitter;
CSplitterWnd m_Splitter2;
}
(Step 2)、重载CMDIChildWnd::OnCreateClient函数,创建切分窗口。
BOOL CMyFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
BOOL btn = m_Splitter.CreateStatic(this,1,2);
btn |= m_Splitter.CreateView(0,0, RUNTIME_CLASS(CAVIDispView), CSize(100,100), pContext);
m_Splitter2.CreateStatic(&m_Splitter, 2, 1,
WS_CHILD | WS_VISIBLE | WS_BORDER,
m_Splitter.IdFromRowCol(0, 1));
btn |= m_Splitter2.CreateView(0, 0, RUNTIME_CLASS(CBMPView), CSize(100,100), pContext);
btn |= m_Splitter2.CreateView(1, 0, RUNTIME_CLASS(CAVIView), CSize(100,100), pContext);
return btn;
//return CMDIChildWnd::OnCreateClient(lpcs, pContext);
}
CFrameWnd::OnCreateClient函数原形为:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CcreateContext * pContext);
缺省的CMDIChildWnd::OnCreateClient函数根据pContext参数提供的信息,调用CFrameWnd::CreateView函数创建一个视。可以重载该函数,加载CCreateContext对象中传递的值,或改变框架窗口主客户区中控制的创建方式。在上面的程序中,笔者 创建了3个切分窗口。比如打开了一个名为“a.avi”的文档,此时该文档将有3个视,一个框架窗口。如果执行了Window/New操作,则此时有一个文档,6个视和2个框架窗口。若该文档调用CDocument::UpdateAllViews函数,则这6个视的CView::OnUpdate函数都会被激发。
(3)、关于CCreateContext的讨论。
CCreateContext是MFC框架所使用的一种结构,它将构成文档/视的组件联系起来。这个结构包括指向文档的指针,框架窗口,视以及文档模板,它还包含一个指向CRuntimeClass的指针,以指明所创建的视的类型。其数据成员如下:
m_pNewViewClass:指向创建上下文的视的CRuntimeClass的指针。
m_pCurrentDoc:指向文档对象的指针,以和新视联系起来。
m_pNewDocTemplate:指向与框架窗口的创建相联系文档模板的指针。
m_pLastView:指向已存在的视,它是新产生的视的模型。
m_pCurrentFrame:指向已存在的框架窗口,它是新产生的框架窗口的模型。
程序员可以通过改变CCreateContext对象的值,来创建更加灵活的视。由于过程较复杂,笔者不再赘许叙,读者可参阅相关的Visual C++ Help文档。
(五)、结束语
Visual C++ 6.0的文档/视结构代表了一种新的程序设计方式,其核心是文档与视的分离,即数据存放与显示(操作)的分离。在MFC类库中,各个对象之间的关系很复杂,但,只要深入了解后,会发现它们之间是相互联系的,可以相互存取的。如果大家想设计出灵活、健壮的应用程序,就必须深入了解MFC。跟踪原代码就是一个较好的方法。文档/视的关系的确非常复杂,如果能知道每个函数是在哪调用的,执行了何种操作,就能游人刃有余,写出优美的应用程序。
200 个类,数千个函数,加之Microsoft 公司隐藏了一些技术细节,使得人们深入学习MFC变得十分困难。
MFC的AppWizard可以生成三种类型的应用程序:基于对话框的应用、单文档应用(SDI)和多文档应用(MDI)。前两者的结构较简单,本文不再赘叙。笔者拟从MFC中的文档/视结构入手,分析一些函数的流程,并解决编制MDI 应用程序过程中的一些常见问题。
(一)、了解文档/视结构
MFC应用程序模型历经多年以有了相当大的发展。有一个时期,它只是个使用应用程序对象和主窗口对象的简单模型。在这个模型中,应用程序的数据作为成员变量保持在框架窗口类中,在框架窗口的客户区中,该数据被提交显示器。随着MFC2。0的问世,一种应用程序结构的新方式----MFC文档/视结构出现了。在这种结构中,CFrameWnd繁重的任务被委派给几个不同类,实现了数据存储和显示的分离。一般情况下,采用文档/视结构的应用程序至少应由以下对象组成:
。应用程序是一个CwinApp派生对象,它充当全部应用程序的容器。应用程序沿消息映射网络分配消息给它的所有子程序。
。框架窗口是一CfrmeWnd派生对象。
。文档是一个CDocument派生对象,它存储应用程序的数据,并把这些信息提供给应用程序的其余部分。
。视窗是Cview派生对象,它与其父框架窗口用户区对齐。视窗接受用户对应用程序的输入并显示相关联的文档数据。
通常,应用程序数据存在于简单模型中的框架窗口中。在文档/视方式中,该数据移入称为document的独立数据对象。当然,文档不一定是文字,文档是可以表现应用程序使用的数据集的抽象术语。而用户输入处理及图形输出功能从框架窗口转向视图。单独的视窗完全遮蔽框架窗口的客户区,这意味着即使程序员直接绘画至框架窗口的客户区,视图仍遮蔽绘画,在屏幕上不出现任何信息。所以输出必须通过视图。框架窗口仅仅是个视图容器。
CDocument类对文档的建立及归档提供支持并提供应用程序用于控制其数据的接口。MDI应用程序可以处理多个类型的文档,每个类型的文档拥有一个相关联的文档模板对象。文档对象驻留在场景后面,提供由视图对象显示的信息。文档至少有一个相关联的视图。视图只能与一个文档相关联。
在文档/视方式中,对象的建立是由文档模板来管理的,它是CDocTemplate派生对象,建立并维护框架窗口,文档及视。
MFC调用命令处理程序以响应发生在应用程序中的事件。命令发送的优先级是:
活动的视图->框架窗口->文档->应用程序->默认窗口过程(DefWindowsProc)
总之,在文档/视方式中,文档和视是分离的,即:文档用于保存数据,而视是用来显示这些数据。文档模板维护它们之间的关西。这种文档/视结构在开发大型软件项目时特别有用。
(二)、了解与文档/视结构有关的各种类之间的关系。
在文档/视应用程序中,CWinApp对象拥有并控制文档模板,后者产生文档、框架窗口及视窗。
从用户的角度来看,“视”实际上是一个普通的窗口。象其他基于Widnows应用的窗口一样,人们可以改变它的尺寸,对它进行移动,也可以随时关闭它。若从程序员的角度来看,视实际上是一个从MFC类库中的Cview类所派生出的类的对象。文档对象是用来保存数据的,而视对象是用来显示数据的,并且允许对数据进行编辑。SDI或MDI的文档类是由Cdocument类派生出来的,它可以有一个或多个视类,而这些视类最终都是由Cview类派生出来的。视对象只有一个与之相联系的文档对象,它所包含的CView::GetDocument函数允许应用在视中得到与之相联系的文档,据此,应用程序可以对文档类成员函数及公共数据成员进行访问。如果视对象接受到了一条消息,表示用户在编辑控制中输入了新的数据,此时,视就必须通知文档对象对其内部数据进行相应的更新。
如果文档数据发生了变化,则所有的视都必须被通知到,以便它们能够对所显示的数据进行相应的更新。Cdocument::UpdateAllViews函数即可完成此功能。当该函数被调用时,派生视类的CView::OnUpdate函数被触发。通常OnUpdate函数要对文档进行访问,读取文档数据,然后再对视的数据成员或控制进行更新,以便反映出文档的变化。另外,还可以利用OnUpdate函数使视的部分客户区无效,以便触发Cview::OnDraw函数,利用文档数据来重新对窗口进行绘制。
在MDI应用程序中,可以处理多个文档类型,即多个文档模板,每个模板又可以有多个文档,每个文档又可以多视显示。为管理方便,上一级往往保留了下一级的指针列表。解释如下:
(1)、每个应用程序类(CwinApp的派生类)都保留并维护了一份所有文档模板的指针列表,这是一个链表结构。应用程序为所要支持的每个文档类型动态分配一个CMultiDocTemplate 对象,
CmultiDocTemplate(UINT nIDResource,
CruntimeClass * pDocClass,
CruntimeClass * pFrameClass,
CruntimeClass * pViewClass );
并在应用程序类的CWinApp::InitInstance成员函数中将每个CMultiDocTemplate对象传递给CWinApp::AddDocTemplate。 该函数将一个文档模板加入到应用程序可用文档模板的列表中。函数原形为:
void AddDocTemplate(CdocTemplate * pTemplate);
应用程序可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板的位置,利用该值来调用CWinApp::GetNextDocTemplate函数,获得第一个CDocTemplate对象指针。函数原形如下:
POSITION GetFirstDocTemplate( ) const;
CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;
第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象指针检索的值。通过这两个函数,应用程序可以遍历整个文档模板列表。如果被检索的文档模板是模板列表中的最后一个,则pos参数被置为NULL。
(2)、一个文档模板可以有多个文档,每个文档模板都保留并维护了一个所有对应文档的指针列表。应用程序可以用CDocTemplate::GetFirstDocPosition函数获得与文档模板相关的文档集合中第一个文档的位置,并用POSITION值作为CDocTemplate::GetNextDoc的参数来重复遍历与模板相关的文档列表。函数原形为:
viaual POSITION GetFirstDocPosition( ) const = 0;
visual Cdocument *GetNextDoc(POSITION & rPos) const = 0;
如果列表为空,则rPos被置为NULL.
(3)、在文档中可以调用CDocument::GetDocTemplate获得指向该文档模板的指针。函数原形如下:
CDocTemplate * GetDocTemplate ( ) const;
如果该文档不属于文档模板管理,则返回值为NULL。
(4)、一个文档可以有多个视。每一个文档都保留并维护一个所有相关视的列表。CDocument::AddView将一个视连接到文档上,将该视加入到文档相联系的视的列表中,并将视的文档指针指向该文档。当有File/New、File/Open、Windows/New或Window/Split的命令而将一个新创建的视的对象连接到文档上时, MFC会自动调用该函数,框架通过文档/视的结构将文档和视联系起来。当然,程序员也可以根据自己的需要调用该函数。
Virtual POSITION GetFirstViewPosition( ) const;
Virtual CViw * GetNextView( POSITION &rPosition) cosnt;
应用程序可以调用CDocument::GetFirstViewPosition返回与调用文档相联系的视的列表中的第一个视的位置,并调用CDocument::GetNextView返回指定位置的视,并将rPositon的值置为列表中下一个视的POSITION值。如果找到的视为列表中的最后一个视,则将rPosition置为NULL.
当在文档上新增一个视或删除一个视时,MFC会调用OnChangeViewList函数。如果被删除的视是该文档的最后一个视,则删除该文档。
(5)、一个视只能有一个文档。在视中,调用CView::GetDocument可以获得一个指向视的文档的指针。函数原形如下:
CDocument *GetDocument ( ) const;
如果该视不与任何文档相,则返回NULL.
(6)、MDI框架窗口通过调用CFrameWnd::GetActiveDocument 可以获得与当前活动的视相连的CDocument 指针。函数原形如下:
virtual CDocument * GetActiveDocument( );
(7)、通过调用CFrameWnd::GetActiveView 可以获得指向与CFrameWnd框架窗口连接的活动视的指针,如果是被CMDIFrameWnd框架窗口调用,则返回NULL。MDI框架窗口可以首先调用MDIGetActive找到活动的MDI子窗口,然后找到该子窗口的活动视。函数原形如下:
virtual Cdocument * GetActiveDocument( );
(8)、MDI框架窗口通过调用CFrameWnd::GetActiveFrame, 可以获得一个指向MDI框架窗口的活动多文档界面子窗口的指针。
(9)、CMDIChildWnd调用GetMDIFrame获得MDI框架窗口(CMDIFrameWnd)。
(10)、CWinApp 调用AfxGetMainWnd得到指向应用程序的活动主窗口的指针。
下面一段代码,就是利用CDocTemplate、CDocument和CView之间的存取关系,遍历整个文档模板、文档以及视。
CMyApp * pMyApp = (CMyApp *)AfxGetApp();
POSITION p = pMyApp->GetFirstDocTemplatePosition();
while(p!= NULL)
{
CDocTemplate * pDocTemplate = pMyApp->GetNextDocTemplate(p);
POSITION p1 = pDocTemplate->GetFirstDocPosition();
while(p1 != NULL)
{
CDocument * pDocument = pDocTemplate->GetNextDoc(p1);
POSITION p2 = pDocument->GetFirstViewPosition();
while(p2 != NULL)
{
CView * pView = pDocument->GetNextView(p2);
}
}
}
(图4)、遍历整个文档模板、文档和视
在应用程序的任何地方,程序员都可以调用AfxGetApp( )获得应用程序的对象指针。由于本文着重介绍文档/视的关系,至于框架窗口之间的关系没能列全,读者可以查相应的文档。
(三)、了解CwinApp::OnFileNew、CwinApp::OnFileOpen和Window/New的程序流程。
(1)、CwinApp::OnFileNew和CwinApp::OnFileOpen函数的简单流程。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
在CWinApp::OnFile/new 或CwinApp::OnFileOpen函数中,核心操作是CDocTemplate::OpenDocument函数。其函数原型为:
virtual CDocument* CDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
BOOL bMakeVisible =TRUE ) = 0;
图(4)中星号标注之后即是该函数的流程,简要介绍如下:
(1)、CDocTemplate::CreateNewDocument函数创建一个新文档,其类型与文档模板相关,并通过函数CDocTemplate::AddDocument加入该文档模板的文档指针列表中。此时,文档类的构造函数被执行,程序可以在此进行文档的初始化。
(2)、函数CDocTemplate::CreateNewFrame调用MDI子窗口类(CMDIChildWnd)的构造函数,生成MDI子窗口对象。接着调用CMDIChildWnd::PreCreateWindow。然后,生成一个CCreateContext对象,(CcreateContext是MFC框架所使用的一种结构,它将构成文档和视的组件联系起来。后文将详细介绍之。)并将该对象值传给CMDIChildWnd::OnCreateClient函数。MFC调用此函数,用CCreateContext对象提供的信息创建一个或多个CView对象。此时,各视的构造函数被依次调用。
(3)、接着,判断lpszPathName是否为空。分为两种情况:
(a)、若为空,则表明要创建一个新文档:调用SetDefaultTitle函数装载文档的缺省标题,并显示在文档的标题栏中;然后执行CDocument::OnNewDocument。该函数调用DeleteContents以保证文档为空,然后置新文档为清洁。可以重载该函数。
(b)、否则,表明要打开一个已存在的文档:调用CDocument::OnOpenDocument打开指定的文件;执行DeleteContext,保证文档为空;调用CObject::Serialize读入该文件的内容。(程序员可在此进行文件的读入操作。当然,也可以在CDocument::OnOpenDocument中读入文件)。然后置文档为清洁;最后,调用CDocTemplate::SetPathName,并把文件名加入到最近文件列表中。
(4)、调用CDocTemplate::InitialUpdateFrame函数,使框架窗口中的各个视收到OnInitialUpdate调用。框架窗口的主视(子窗ID等于AFX_IDW_PANE_FIRST的视)被激活。程序员可以在此对视对象进行初始化。
(2)、Window/New命令的程序流程
当主框架窗口上有子窗口时,选择Window/New命令可以生成该活动子窗口的影象。它们有相同的文档模板、相同的文档。其流程如下:
执行Window/New的过程与File/New的过程差不多。所不同的是,File/New须要创建一个新文档,而Window/New则是获得已存在的MDI子窗口的文档。因此以前存在的视和New以后生成的视均为该文档的视,都是该文档的内容的显示。当调用CDocument::UpdateAllViews函数时,它们(视)的OnUpdate函数都将被激活。此时,在该文档的视指针列表中,将有多于一个的视(具体数目视Window/New执行的次数而定)。读者可以利用(图3)中的代码跟踪程序结果。
(四)、几种情况的讨论
上面,笔者就MFC中文档/视的关系进行了分析,下面,笔者将结合具体情况进行讨论:
(1)、如何根据自己的要求来选择文档模板,及相应的视和文档。
在通常的MDI应用程序中,只有一个文档模板,程序员只能打开一种类型的文档。因此,程序员只要调用File/New或者File/Open创建或者打开文档即可,至于文档、视和框架窗口之间的关系,由文档模板在幕后控制,不须要对文档模板进行操作。但是,如果应用程序需要处理多种类型的文档,并且何时打开何种文档均需程序员手工控制,此时,程序员必须对文档模板进行编程。
例如,笔者需要处理AVI和BMP两种文件类型。AVI和BMP的数据存放格式不同,不能用同一的数据结构来描述,因此,把它们的数据都存入一个文档是不合适的。同时,由于AVI是图象序列,BMP仅是一幅图象,它们的显示是肯定不一样的,即它门的视不同。基于此,笔者决定分别建立两套文档模板,两套框架窗口,两套文档和两套视,分别用于AVI和BMP的数据存放和显示。程序可以根据用户选择的文件名来分别处理AVI和BMP。具体步骤如下:
(Step 1)、在应用程序类(CWinApp)的派生类中增加文档模板成员变量,以便对文档模板进行操作。
class C3dlcsApp : public CWinApp
{
......
public:
CMultiDocTemplate * m_pAVIDocTemplate;
CMultiDocTemplate * m_pBMPDocTemplate;
}
(Step 2)、在主框架中增加菜单响应:
void CMainFrame::OnFileOpen()
{
CFileDialog my(true);
if(my.DoModal()==IDOK)
{
CString FileName = my.GetPathName();
CString FileExt = my.GetFileExt();
if((FileExt == AVI) || (FileExt == avi))
{
CMyApp * pMyApp = (CMyApp *)AfxGetApp();
CMultiDocTemplate*pAVIDocTemplate=pMyApp->m_pAVIDocTemplate;
pAVIDocTemplate->OpenDocumentFile(FileName);
}
else if((FileExt == BMP) || (FileExt == bmp))
{
CMyApp * p3dlcsApp = (CMyApp *)AfxGetApp();
CMultiDocTemplate* pDATDocTemplate=pMyApp->m_pBMPDocTemplate;
pDATDocTemplate->OpenDocumentFile(FileName);
}
else
{
AfxMessageBox(Yor select a file not supported!);
return;
}
}
}
笔者把用户输入文件名的后缀作为分支条件,如果是AVI文件,则先获得关于AVI文件的文档模板,然后调用CDocTemplate::OpenUpdateFrame (lpszFileName)函数打开此文档。正如前面所分析,此函数将依次生成新文档,新框架,在CMDIChildWnd::OnCreateClient中创建视,最后向框架中所有的视发送初始化消息,使其显示在屏幕上。如果是BMP文件,操作类似。
当然,程序员也可以在程序的任何位置实现此操作:通过全局函数AfxGetApp 获得应用程序对象指针,从而获得相应的文档模板指针。
由于由AppWizard生成的应用程序会缺省调用CWinApp::OnFileNew,所以当程序开始执行时,会在主框架上显示一个新的空窗口。如果想去掉这个空窗口,只须重载CWinApp::OnFileNew函数,不许要任何代码,即可。
(2)、切分窗口与文档/视结构
一个文档可以有多个视,切分窗口即是表示多视的一种方法。 切分窗口是通过类CSplitterWnd来表示的,对Window来说,CSplitterWnd对象是一个真正的窗口,它完全占据了框架窗口的客户区域,而视窗口则占据了切分窗口的窗片区域。切分窗口并不参与命令传递机制,(窗片中)活动的视窗从逻辑上来看直接被连到了它的框架窗口中。
切分窗口可以分为动态和静态两种。前者较简单,本文仅讨论后者。创建切分窗口的步骤如下:
(Step 1)、在自己的框架窗口中声明成员变量,用以对切分窗口进行操作。
class CMyFrame : public CMDIChildWnd
{
......
CSplitterWnd m_Splitter;
CSplitterWnd m_Splitter2;
}
(Step 2)、重载CMDIChildWnd::OnCreateClient函数,创建切分窗口。
BOOL CMyFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
BOOL btn = m_Splitter.CreateStatic(this,1,2);
btn |= m_Splitter.CreateView(0,0, RUNTIME_CLASS(CAVIDispView), CSize(100,100), pContext);
m_Splitter2.CreateStatic(&m_Splitter, 2, 1,
WS_CHILD | WS_VISIBLE | WS_BORDER,
m_Splitter.IdFromRowCol(0, 1));
btn |= m_Splitter2.CreateView(0, 0, RUNTIME_CLASS(CBMPView), CSize(100,100), pContext);
btn |= m_Splitter2.CreateView(1, 0, RUNTIME_CLASS(CAVIView), CSize(100,100), pContext);
return btn;
//return CMDIChildWnd::OnCreateClient(lpcs, pContext);
}
CFrameWnd::OnCreateClient函数原形为:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CcreateContext * pContext);
缺省的CMDIChildWnd::OnCreateClient函数根据pContext参数提供的信息,调用CFrameWnd::CreateView函数创建一个视。可以重载该函数,加载CCreateContext对象中传递的值,或改变框架窗口主客户区中控制的创建方式。在上面的程序中,笔者 创建了3个切分窗口。比如打开了一个名为“a.avi”的文档,此时该文档将有3个视,一个框架窗口。如果执行了Window/New操作,则此时有一个文档,6个视和2个框架窗口。若该文档调用CDocument::UpdateAllViews函数,则这6个视的CView::OnUpdate函数都会被激发。
(3)、关于CCreateContext的讨论。
CCreateContext是MFC框架所使用的一种结构,它将构成文档/视的组件联系起来。这个结构包括指向文档的指针,框架窗口,视以及文档模板,它还包含一个指向CRuntimeClass的指针,以指明所创建的视的类型。其数据成员如下:
m_pNewViewClass:指向创建上下文的视的CRuntimeClass的指针。
m_pCurrentDoc:指向文档对象的指针,以和新视联系起来。
m_pNewDocTemplate:指向与框架窗口的创建相联系文档模板的指针。
m_pLastView:指向已存在的视,它是新产生的视的模型。
m_pCurrentFrame:指向已存在的框架窗口,它是新产生的框架窗口的模型。
程序员可以通过改变CCreateContext对象的值,来创建更加灵活的视。由于过程较复杂,笔者不再赘许叙,读者可参阅相关的Visual C++ Help文档。
(五)、结束语
Visual C++ 6.0的文档/视结构代表了一种新的程序设计方式,其核心是文档与视的分离,即数据存放与显示(操作)的分离。在MFC类库中,各个对象之间的关系很复杂,但,只要深入了解后,会发现它们之间是相互联系的,可以相互存取的。如果大家想设计出灵活、健壮的应用程序,就必须深入了解MFC。跟踪原代码就是一个较好的方法。文档/视的关系的确非常复杂,如果能知道每个函数是在哪调用的,执行了何种操作,就能游人刃有余,写出优美的应用程序。
发表评论
-
调薪感悟
2018-04-29 06:54 0这次调薪我非常不满意,原因如下: 1.提升的 ... -
领导必备素质
2015-09-08 13:08 1510工作多年,接触过的领导不下十几位,发现他们身上都共有的特点 ... -
(转) 没用的抱怨
2014-06-13 17:38 01 、我没有口才——错 ... -
打工与创业的区别
2011-11-16 16:04 1746打工与创业有什么区别 ... -
公钥和私钥
2011-06-30 08:11 18341,公钥和私钥成对出现 ... -
说的对
2011-06-01 20:18 1089不要评价别人的容貌,因为他不靠你吃饭。不要评价别人的德行,因为 ... -
Outlook Express \Foxmail \Microsoft Office Outlook设置
2011-05-12 08:53 2072Outlook Express 设置 ... -
人生没有退路
2011-05-11 12:27 2074只有一条路可 ... -
护肤品
2011-03-15 12:46 1380买护肤品注意事项 默 ... -
中国人正在上的四大当,你上几个了?
2011-02-16 12:40 1795第一大当: ... -
如何在面试中发现优秀程序员
2011-02-12 13:30 1753我曾在一次面试中要求一个很有经验的嵌入式软件开发人 ... -
一个解除TCP连接的TIME_WAIT状态限制的方法
2011-01-25 16:11 6258近日无意间发现了一个小窍门:当TCP连接所对应s ... -
window.open()用法记录
2011-01-18 21:43 1355window.open()的所有参数列表-PHP教程,PHP应 ... -
PHP编辑器
2011-01-17 11:01 1588常用PHP编辑器下载 看你在用哪个 来源:站长下吧 时间:2 ... -
计算机十二种常用密码破解法
2010-11-10 11:46 1638在日常操作中,我们经 ... -
RS232中RTS和CTS的作用 转帖
2010-10-31 23:35 1910RS232中RTS和CTS的作用 ... -
中国各省名字由来
2010-10-29 12:34 1758山东:以在太行山之东 ... -
99%的人不了解的真实中国历史
2010-10-09 12:55 12631、秦始皇灭了六国,但并未统一中国,因为当时还有一个卫国。评价 ... -
比较老的脑筋急转弯
2010-10-09 12:25 14851、谁是万兽之王?答案:动物园园长2、什么样的人死后还会出现? ... -
30个搞笑的方法
2010-10-09 12:23 29121]一个朋友,让他先说3遍"老鼠",然后再 ...
相关推荐
**VC++6.0在Windows 7环境下的兼容性与使用指南** Visual C++ 6.0(简称VC++6.0)是微软发布的一款经典且功能强大的C++集成开发环境,它在20世纪90年代末期推出,成为许多开发者编写Windows应用程序的首选工具。...
1. **官方文档**:虽然年代久远,但微软提供的官方文档仍然是学习VC++6.0的重要参考资料。 2. **教程书籍**:如《Effective C++》、《More Effective C++》等,帮助理解C++语言的精髓和最佳实践。 3. **在线社区**:...
VC++6.0 是一个功能强大且广泛应用的集成开发环境(IDE),本教程旨在指导读者从零开始学习和掌握 VC++6.0 的使用。下面是 VC++6.0 入门教程的概述: 1. 桌面上的快捷方式:双击快捷方式可以直接运行 VC++6.0。 2....
在本项目中,"VC++6.0大作业.zip"是一个包含了一位学生在学习VC++6.0课程时完成的期末大作业的压缩文件。这个作业是一个简易的调查问卷应用,不仅具备基本的用户交互功能,还增加了背景音乐、背景颜色以及按钮样式的...
学习VC++6.0可以从以下几个方面入手: 1. **基础语法**:理解C++的基本语法,包括变量、数据类型、控制结构、函数等。 2. **面向对象编程**:C++是面向对象的语言,掌握类、对象、继承、多态和封装的概念。 3. **MFC...
**VC++6.0免安装绿色版**是微软...总的来说,VC++6.0免安装绿色版以其简洁高效的特点,成为了许多学习和研究C++的初学者首选的开发工具,尽管现代有更多先进的IDE如Visual Studio,但VC++6.0的经典地位依然不可替代。
对于初学者来说,VC++6.0是学习C/C++语言的良好平台,因为它有直观的界面和丰富的帮助文档。 1. **IDE集成开发环境**:VC++6.0的集成开发环境提供了一站式的代码编写、编译、调试功能。用户可以在同一界面下完成...
这款软件在20世纪90年代末期发布,虽然之后有更新版本如Visual Studio 2019等,但VC++6.0因其轻量级、简洁的界面以及对旧版代码的良好支持,至今仍受到很多开发者和学习者的青睐,尤其是在教学和计算机等级考试中。...
- "如何学习VC++6.0.exe":这是一个可能包含交互式教程或演示的可执行文件,可以帮助您更直观地了解VC++6.0的使用。 - "如何使用VC++6.0.txt":这可能是详细的文字教程,涵盖了更多具体操作步骤和技巧。 总的来说...
西西软件园是一个提供软件下载和教程的平台,用户可以在这里找到VC++6.0的其他版本、补丁、教程等资源,帮助用户更好地使用和学习VC++6.0。 **C++编程知识** C++是一种面向对象的编程语言,它是C语言的扩展,增加...
学习VC++6.0可以帮助理解C++的基础语法、面向对象编程概念,以及如何利用MFC构建Windows应用程序。不过,需要注意的是,VC++6.0不支持后来的C++标准,例如C++11、C++14及更高版本,因此在实际生产环境中,可能需要...
VC++6.0 入门教程 VC++6.0 是 Microsoft 公司推出的一个功能强大且广泛使用的集成开发环境(IDE),它提供了一个全面的开发平台,允许开发者创建Windows应用程序、控制台应用程序、DLL库、静态库等各种类型的项目。...
尤其对于学习C++基础、理解MFC(Microsoft Foundation Classes)库的初学者来说,VC++6.0是一个不错的选择。 **二、安装过程** 1. **下载与解压**:首先需要找到适用于Win10系统的VC++6.0绿色版压缩包,将其下载并...
总之,VC++ 6.0 和MSDN中文版是学习和开发Windows应用的宝贵资源,尤其是对于初学者来说,提供了友好且实用的学习环境和资料。通过深入学习和实践,开发者能够掌握C++编程以及Windows平台开发的关键技能。
"C语言上机软件VC++6.0中文版安装_学习Visual_C++_6.0编程环境的使用借鉴" 本文主要介绍了Visual C++ 6.0(简称VC)的安装和使用方法,并对VC的编程环境进行了详细的介绍。下面是从文中提取的相关知识点: 1. VC++...
总结来说,学习VC++6.0的用户界面制作不仅是对MFC框架的理解,还包括对资源编辑器的掌握,控件的使用,以及消息处理机制。通过不断的实践和学习,开发者可以创造出功能强大、易于使用的Windows应用程序。
MSDN图书馆对于学习和使用微软的开发工具,如VC++6.0,是非常重要的参考资料。 在本压缩包文件“VC++6.0附MSDN”中,"附MSDN"表示VC++6.0安装包中包含了MSDN的相关资料,这意味着用户在安装后可以直接通过内置的...
这个版本在当时非常流行,尽管现在有了更新的版本如Visual Studio,但VC++6.0仍然是初学者学习C++编程的重要工具,因其界面简洁,功能强大,且包含了MFC(Microsoft Foundation Classes)库,便于开发Windows应用。...
《VC++6.0培训教程》是一本专为学习Visual C++ 6.0编程语言设计的教材,由西南交通大学的周颖恒编著。这本书旨在帮助初学者和有一定基础的程序员逐步掌握VC++6.0的核心概念和技术,通过深入浅出的讲解,使读者能够...
总的来说,VC++ 6.0是学习C++编程的一个良好起点,尤其适合初学者。它的集成开发环境(IDE)提供了完整的开发流程,从编写代码到运行调试,一应俱全。然而,随着技术的发展,学习和使用更现代的开发工具和C++标准也...