重貼:MFC類中獲得其它類指標 (轉)

worldblog發表於2007-12-04
重貼:MFC類中獲得其它類指標 (轉)[@more@] 

MFC類中獲得其它類指標

成都:蘇穎鋒
(vcmfc輸入並轉貼)

  當用VC++的Application Wizard生成除了CDialog Basiced以外的應用時,將自動產生檢視類、文件類、主幀視窗類、應用程式類等等。一般來說,程式的核心資料及操作在文件類中實現。跟介面有關的資料及操作在檢視類中實現。當需要在某個類中使用不屬於該類的資料時,必須要取得該資料所屬類的指標。從檢視類獲得文件類的指標是很容易的,用GetDocument即可,這在一般的MFC文件中有介紹,也是中極為常用的的操作,比如檢視類在進行重畫等操作時,往往要用到文件類中的資料。然而只能從檢視類獲得文件類的指標是遠遠不夠的,每個類都有獲得其它各個類指標的一套方法,現歸納如下:

  為方便說明,現假設已用Application Wizard生成一個SDI應用程式Test,包含如一幾個類:CTestApp,CTestDoc,CTestView,CMainFrm.

1.從檢視類獲得文件類的指標

  如前所述,在檢視類中需要引用文件類的地方之前,使用以下語句:
 CTextDoc *pDoc=(CTestDoc*)GetDocument();
以後便可使用pDoc指標訪問文件類。
此處的強制型別轉換在Test應用程式中並不必需,因為該程式中只有一個檢視類,並且在Initstance()中用SDI文件模板進行了裝配,你可以在Test.cpp中的Initstance()方法中看到以下語句:
  CSingleDocTemplate *pDocTemplate;
  pDocTemplate=new CSingleDocTemplate(IDR_MAINFRAME,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()和GetNextView(),具體語法如下:
  virtual POSITION GetFirstViewPosition() const;
  virtual CView* GetNextView(POSITION& rPosition) const;
  注意:GetNextView()括號中的引數用的是引用方式,因此後值可能改變。
  GetFirstViewPosition()用於返回第一個檢視位置(返回的並非檢視類指標,而是一個POSITION型別值),GetNextView()有兩個功能:返回下一個檢視類的指標以及用引用調動的方式來改變傳入的POSITION型別引數的值。很明顯,在Test程式中,只有一個檢視類,因此只需將這兩個函式一次即可得到CTestView的指標如下(需定義一個POSITION結構變數來輔助操作):
  CTestView* pTestView;
  POSITION pos=GetFirstViewPosition();
  pTestView=GetNextView(pos);
這樣,便可到了CTestView類的指標pTestView.執行完成幾句後,變數pos=NULL,因為沒有下一個檢視類,自然也沒有下一個檢視類的POSITION.

但是之幾條語句太簡單,不具有太強的通用性和特徵;當象前面說的那樣,當要在多個檢視為中返回某個指定類的指標時,我們需要遍歷所有檢視類,直到找到指定類為止。判斷一個類指標指向的是否某個類的例項時,可用IsKindOf()成員函式時行檢查,如:
  pView->IsKindOf(RUNTIME_CLASS(CTestView));
  即可檢查pView所指是否是CTestView類。
有了以上基礎,我們已經可以從文件類取得任何類的指標。為了方便,我們將其作為一個文件類的成員函式,它有一個引數,表示要獲得哪個類的指標。實現如下:
  CView* CTestDoc::GetVieww(CRuntimeClass* pClass)
  {  CView* pView;
  POSITION pos=GetFirstViewPosition();
  while(pos!=NULL)
  {
  pView=GetNextView(pos);
  if(pView->IsKindOf(pClass))
  break;
  }
  if(!pView->IsKindOf(pClass))
  return  NULL;
  return pView;}
  其中用了兩次檢視類的成員函式IsKindOf()來判斷,是因為退出while迴圈有三種可能:
  1.pos為NULL,即已經不存在下一個檢視類供操作;
  2.pView已符合要求。
  3.1和2同是滿足。這是因為GetNextView()的功能是將當前檢視指標改變成一個檢視的位置同時返回當前檢視指標,因此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=(CTestDoc*)GetDocument();
  CView* pView;
  POSITION pos=pDoc->GetFirstViewPosition();
  while(pos!=NULL)
  {
  pView=pDoc->GetNextView(pos);
    if(pView->IsKindOf(pClass))
    break;
  }
  if(!pView->IsKindOf(pClass))
  return  NULL;
  return pView;}
  這個函式和2中的GetView()相比,一是多了第一句以取得文件類指標,二是在GetFirstViewPosition()和GetNextView()前加上了文件類指標,以表示它們是文件類成員函式。
  有了此函式;當要從CTestAView中取得CTestBView的指標時,只需如下:
  CTestBView* pTestbView=(CTestView*)GetView(RUNTIME_CLASS(CTestBView));

4. 從主幀視窗類獲得檢視類指標

  對本文所舉的Test這各SDI程式來說,這是簡單的,只需用CFrameWnd類的GetActiveView()成員函式即可。格式如下:
  CFrameWnd::GetActiveView()
  但將此函式應用在MDI應用的CMDIFrameWnd為中時,並不象所想的那樣獲得當前活動子視窗的檢視類,而是返回NULL,這是一個要領性問題。在MDI程式中,CMDIFrameWnd沒有和任何檢視類發生關係,也就是說沒有檢視類直接屬於它,只有子幀視窗類CMDIChildWnd才是所有子視窗檢視類的父視窗。而子幀視窗的父視窗才是CFrameWnd。因此,在MDI程式中獲得活動檢視類的正確方法應為:先獲得活動子幀視窗,再從活動子幀視窗中獲得活動檢視類:
  //獲得活動子幀視窗
  CMDIChildWnd* pChild=(CMDIChildWnd*)GetActiveFrame();
  //或:CMDIChildWnd* pChild=MDIGetActive();
  //獲得活動子幀視窗的活動檢視
  CMyView* pView=(CMyView*)pChild->GetActiveView();

5.從檢視類中獲得主幀視窗類指標:

用函式:CWnd::GetParentFrame()或AfxGetMainWnd();
可達到目的。GetParentFrame()的工作原理是在父視窗鏈中搜尋,直到找到CFrameWnd或其派生類為止,並返回其指標。用法在InfoViewer中有詳細介紹。

6.在任何類中獲得應用程式類
  用MFC全域性函式AfxGetApp()可做到。

7.從應用程式類中獲得主幀視窗類
  CWinThread類有一個資料成員叫m_pMainWnd,由於CWinApp類由CWinThread派生而來,我們的應用程式為又由CWinApp派生而來,所以我們的CTestApp類也有一個m_pMainWnd成員,它所指南的即是CMainFrame類。(需進行合適的強制型別轉換)。

總結起來有幾點注意:
  A.在類A中獲得類B的指標時,類A應包含類B的頭。
  B.在很多時候要進行強制型別轉換,並要注意括號的括法。
  由於派生類和父類指標型別的相容,使明確區分各個類變得十分重要。在拿不準的時候,最好加上強制型別轉換。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-987779/,如需轉載,請註明出處,否則將追究法律責任。

相關文章