MFC類中獲得其他類指針(上)
當用visual c++的Application Wizard生成除了以外的應用程序時,將自動產生視圖類,文檔類,主幀窗口類,應用程序類等等.
一般說來,程序的核心數據及操作在文檔類中實現.跟界面有關的數據及操作在視圖類中實現.當需要
在某個類中使用不屬於該類的數據時,必須要取得概述據所屬的類的指針.從視圖類獲得文檔類的指針
是很容易的,用GetDocument()即可,這在一般的MFC文當中有介紹,也是編程中紀委常用的操作,比如視圖類
在進行重畫等操作時,往往要用到文檔類中的數據.然而只是從視圖類中獲得文檔類得指針是遠遠不夠的,
每個類都有獲得其他類指針的一套方法.現歸納如下.
爲方便說明,現假設已用app wizard生成了一個SDI應用程序test,包含如下幾個類:
CTestApp,CTestDoc,CTestView,CMaimFrame
1:從視圖類獲得文檔類指針
如前所述,在視圖類中需要引用文檔類的地方之前,使用如下語句:
CTestDoc *pDoc=(CTestDoc *)GetDocument();
以後便可使用pDoc指針訪問文檔類.
此處的強制類型轉換在test應用程序中並不需要,因爲該程序只有一個視圖類,並且在InitInstance()
重用SDI文檔模板進行了裝配.你可以在Test.cpp種的InitInstance()方法中看到如下語句:
CSingleDocTemplate *pDocTemplate;
pDocTemplate=new CSingleDocTemplate(IDR_MAINRAME,RUNTIME_CLASS(CTestDoc),RUNTIME_CLASS(CMainFrame),RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);
以及TestView.h中的定義:
inline CTestDoc *CTestView::GetDocument()
{return (CTestDoc *)m_pDocument;}
簡而言之,就是說CTestView的GetDocument()函數自然而然的認爲CTestDoc是與它 "相配 "的.當生成了一個具有多個視圖類的應用程序時,(如用CSplitterWnd將窗口分爲兩欄,但這兩欄並非從同一種視圖類派生就屬於這種情況。具體實現在本文討論範圍之外),只有一個視圖類能與唯一的文檔類用文檔模板進行裝配,那麼在另外一個未經裝配的類中要取得文檔類的指針,則需進行強制類型轉換。
2:從文檔類取得視圖類指針
CDocument類提供了兩個函數用於視圖類的定位: GetFirstViewPosition()和 GetNestView(),
virtual POSITION GetFirstViewPosition() const;
virtua CView * GetNextView (POSITION& rPosition )const;
注意:GetFirstView()括號中的參數用的是引用方式,因此執行後值可能改變.
GetFitstViewPosition()用於返回第一個視圖位置(返回的並非視圖類指針,而是一個POSITION類型值),GetNextView()有兩個功能:返回下一個視圖類的指針以及用引用調用和方式來改變傳入的POSITION類型參數的值。很明顯,在Test程序中,只有一個視圖類,因此只需將這兩個函數調用一次即可得到CTestView的指針如下(需定義一個POSITION結構變量來輔助操作):
CTestView*pTestView;
POSITISONpos=GetFirstViewPosition();
pTestView=GetNextView(pos);
這樣,便得到了CTestView類的指針pTestView.執行完這幾句後,變量pos=NULL,因爲沒有下一個視圖類,自然也沒有下一個視圖類的POSITION.
但是這幾條語句太簡單,不具有太強的通用性和安全特徵,當象前面說的那樣,當要在多個視圖類中返回某個指定類的指針時,我們需要遍歷所有視圖類,直到找到指定類爲止。判斷一個指針指向的是否是某個類的實例時,可用IsKindOf()成員函數是行檢查,如:
pView-> IsKindof(RUNTIME-CLASS(CTestView));
即是檢查pView所指是否是CTestView類。
有了以上基礎,我們可以從文檔類取得任何類的指針。爲了方便,我們將其作爲一個文檔類和成員函數,它有一個參數,表示要獲得哪個類的指針,它返回的就是這個類的指針。實現如下:
CView * CTestDoc:: GetView(CRuntimeClass*pClass)
{CView*pView;POSITIONpos=GitFirstViewPosition();while(pos!=NULL){pView=GitNextView(pos);
if(pView-> IsKindOf(pClass))break;}
if(!pView-> IsKindof(pClass)){AfxMessageBox( "Connot locate the View. ");
return NULL;}
return pView;}
其中用了兩次視圖類的成員函數IsKindOf()判斷,是因爲退出while循環有三種可能:
1.pos爲NULL,即已經不存在下一個視圖供操作;
2.pView已符合要求;
3.1和2同時滿足。這是因爲Get-NextView()的功能是將pos改變成下一個視圖的位置同時返回當前視圖指針,因此pos是pView的下一個視圖類的POSITION,完全有可能即是pos==NULL又是pView符合需要。當所需的視圖是最後一個視圖類時就如此。因此需採用兩次判斷。
使用該函數應遵循如下格式(以取得CTestView指針爲例):
CTestView*pTestView=(CTestView*)GetView(RUNTIME-CLASS(CTestView));
RUNTIME-CLASS()是一個宏,可以簡單地理解它的作用:
將類和名字轉化爲CRuntimeClass類指針。
至於強制類型轉換也是爲了安全特性考慮的,因這從同一個基類派生的類之間以及它們與基類之間的指針類型是互相兼容的。這種強制類型轉換也許並不必要,但能避免一些可能出現的麻煩。
3:從一個視圖類取得另一個視圖類的指針
綜合1和2,很容易得出視圖類之間互相獲得指針的方法:就是用文檔類作中轉,先用1的方法得到文檔類的指針,再用2的方法,以文檔的視圖定位函數:
(假設要從CTestAView中取得指向其它視圖類的打針)
CView*CTestAView::GetView
(CRuntimeClass*pClass)
{CTestDoc*pDoc*pDoc=
(CTestDoc*)GetDecument();
CView*pView;
POSITION pos=pDoc-> GetFistViewPosition();
while(pos!=NULL){
pView=pDoc-> GetNextView(pos);
if( pView-> IsKindOf(pClass) )
break; }
if(!pView-> IsKindOf(pClass)){
AfxMessageBox( "Connot Iocate the View. ");
return NULL;}
return pView;}
這個函數和2中的GetView()相比,一是多了第一名以取得文檔類指針,二是在GetFirstViewPosition()和GetNextView()前加上了文檔類指針,以表示它們是文檔類成員函數。
有了此函數要從CTestAView中取得CTestBView的打針時,只需如下:
CTestBView
CTestBView*pTestBView=(CTestBView*)
GetView(RUNTIME-CLASS(CTestBView));
4。從主幀窗口類獲得視圖類指針
對本文所舉的Test這種SDI程序來說,這是很簡單的,只需用CFrameWnd類的GetAcTIVEvIEW()成員函數即可。格式如下:
CFrameWnd::GetActiveView()
但將此函數應用在MDI應用程序的CMDIFrameWnd類中時,並不象所想的樣獲得當前活動子窗口的視圖類,而是返回NULL。這是一個概念性問題。在MDI程序中,CMDIFrameWnd沒有和任何視圖類發生關係,也就是說沒有視圖類直接隸屬於它,只有子窗口類CMDIChildWnd纔是所有子窗口視圖類的父窗口。而子幀窗口的父窗口才是CFrameWnd。因此,在MDI程序中獲得活動視圖類的正確方法應爲:先獲得活動子幀窗口,再從子幀窗口中獲得活動視圖類:(以下這段代碼與VC++的InfoViewer中GetActiveView的示範代碼類似,並有詳細說明, 限於篇幅,此處不一一說明)
//獲得活動子幀窗口
CMDIChildWnd*pChild=(CMDIChildWnd*)
GetActiveFrame();
//或:CMDIChildWnd*pChild=MDIGetActive();
//獲得活動子幀窗口的活動視圖
CMyView*pView=(CMyView*)pChild-> GetAetiveView();
5從視圖類中獲得主幀窗口類指針
用函數CWnd::GetParentFrame()
可達到目的。它的工作原理是在父窗口鏈中搜索,直到找到 CFrameWnd或其派生類爲止,並返回其指針 。用法在 InfoViewer中有詳細介紹。
6在任何類中獲得應用程序類
用 MFC全局函數AfxGetApp()可做到。
7從應用程序類中獲得生幀窗口類
CWinThread類有一個數據成員叫 m-pMainWnd,由於 CWinApp類由zCWinThread 類派生而來,我們的應用程序類又由CWinApp 派生而來,所以我們的 CTestAPP類也有一個m-pMainWnd 成員, 它所指向的即是CMainFrame 類。(需進行合適的強制類型轉換)。
例:在應用程序類中對生幀窗口類的一個整型變量nIntVar 賦值:
((CMainFrame*)m-pMainWnd)-> nIntVar=100;
幾點注意:
有了以上幾種指針獲取方法,已以因以在類中方便地獲得其它類的指針,使程序更加靈活高效。但有兩點需注意:
1在類 A中獲得類 B的指針時,類 A應包含類B 的頭文件
例:在 CTestView中欲獲得 CMainFrame的指針,應在 TestView.cpp文件頭部加入 #include "MainFrm.h "。否則通不過編譯。
2在很多時候要進行強制類型轉換,並要注意括號的括法
由於派生類和父類指針類型的兼容,使明確區分各個類變得十分重要。在拿不準的時候,最好加上強制類型轉換。比如:
CTestBView*pTestBView=(CTestBView*)
GetView(RUNTIME_CLASS(CTestBView));
使獲得的類指針強 成 CTestBView*型而非 CView*型。
另外由於指針成員運算符“->”的運算優先級高於強制類型轉換,因此括號的定法很重要。見例:
(CMainFrame*)m-pMainWnd-> nIntVar=100;
這樣的寫法是不正確的, 相當於(CMainFrame*) 類型轉換在對nIntVar 進行轉換,將在編譯時出錯,告知無法將整型轉換成CMainFrame* 型。正確的寫法是:
((CMainFrame*)m-pMainWnd)-> nIntVar=100
這無疑上個微不足道的小問題,然而也正因爲它微小且不易覺,如果不注意的話, 可能會浪費掉不少時間.
MFC類中獲得其他類指針
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.