什麼是pod類型

來源:互聯網
上載者:User
原文出處:MSDN Magazine November 2004 (C++ Q&A)

  1. 調用虛擬函數
  2. 持續化檢視狀態
  3. POD 類型概念

問題: 在 C++ 中,無法從某個類的建構函式中調用派生的虛擬函數,因為虛表還沒有完全建立。但是在C#中好像就可以,是這樣嗎?為什麼會有這種差別呢?
Clifton F. Vaughn 答案: 確實如此,在這個方面 C# 與 C++ 是有差別的。在 C++ 中,如果你從建構函式或者解構函式中調用虛擬函數,編譯器調用的虛擬函數是定義在這個正在被構造的類執行個體中的(例如,如果從 Base::Base 中調用 Base::SomeVirtFn ),不是最底層派生的執行個體(the most derived instance),正像你說的那樣,因為在最底層派生的建構函式執行之前,虛表還沒有完全被初始化。另一種說法是衍生類別還沒有被建立。


Figure 2 虛擬函數 TestSimilarly

  當你從解構函式中調用虛函數時,C++ 調用該基類的解構函式,因為衍生類別已經被銷毀(其析構已經被調用)。雖然這個行為可導致異常結果(此即為什麼從建構函式或解構函式中調用虛函數被認為是糟糕的編程實踐的原因),它是大多數 C++ 程式員必須瞭然於心的基本常識。
  正如你所指出的那樣,在 C# 有所不同。託管對象——無論是在 C#,託管 C++ 中,還是任何其它的 .NET 相容語言中——是作為其最終類型被建立的,也就是說,如果你從建構函式或解構函式中調用虛函數,系統調用的是最末層派生的函數。Figure 1 所示程式舉例說明了這一點。如果你編譯並運行這個程式,你會看到 Figure 2 所示輸出。
  這種行為對於 C++ 程式員來說似乎有些奇特。它意味著在衍生類別被初始化之前,你可以調用某個衍生類別型的虛擬函數——也就是說在其建構函式運行之前。同樣,如果你從基類解構函式中調用虛函數,該函數是在衍生類別被銷毀之後啟動並執行——也就是說在解構函式被調用之後。那麼先不說這種差別存在的原因,剛才不是還說從建構函式/解構函式中調用虛函數被認為是糟糕的實踐。
  為什麼微軟的傢伙們要像這樣來設計 C# 呢?因為它簡化了記憶體管理。垃圾收集器為了釋放記憶體,它需要知道對象有多大。如果 C# 像 C++ 那樣構造對象,那麼你可能會碰到這樣一種情況:有兩個對象,Obj1 和 Obj2,下面這兩條語句都為真:

typeof(Obj1)==typeof(Obj2) sizeof(Obj1)!= sizeof(Obj2) 

  因為對象之一是被部分構造。(不要忘了垃圾收集器是非同步啟動並執行。)通過將物件建構成最終類型,垃圾收集器能從其類型決定對象的大小。如果 C# 像 C++ 那樣進行部分構造,則垃圾收集器將需要更多的代碼來決定部分構造對象的真實大小。這樣將帶來複雜性和效能下降,首先要解決這個問題很讓人氣餒,所以為了較快的垃圾收集利益,微軟的傢伙們決定像上面那樣來實現 C#。有關這方面的討論參見 Raymond Chen 的 blog:“The Old New Thing”。

問題: 在 2004 三月的專欄中,你展示了如何改變檔案開啟對話方塊的最新檢視狀態設定,但沒有涉及到儲存這個使用者使用的最新視圖設定。我遇到的問題是讀取使用者已有的開啟檔案對話方塊設定。我只找到直接讀取列表框資訊的方法,但當使用者選擇縮圖模式時,那樣做不能得到正確的資訊。對此你有沒有解決辦法?

Maarten van Dillen   問題: 我正在用公用的 CFileDialog 類做開發,應該不是很難,但事情似乎並不是那樣。我想強制檔案開啟對話方塊的視圖模式為縮圖。我要用 Visual C++ 來做,你能否提供一些建議? Elliot Leonard  

答案: 有幾個讀者都在問檔案開啟對話方塊中的縮圖問題。在我三月份的專欄中,我示範了如果向檔案開啟對話方塊中的 SHELLDLL_DefView 專用視窗發送 WM_COMMAND 訊息以設定不同的視圖模式——但你如何知道當前所處的模式是哪一個呢?你必須擷取清單控制項並調用 CListCtrl::GetView:

// in dialog classHWND hlc = ::FindWindowEx(m_hWnd, NULL, _T("SysListView32"), NULL);CListCtrl* plc = (CListCtrl*)CWnd::FromHandle(hlc);DWORD dwView = plc->GetView();

  CListCtrl::GetView 返回 LV_XXX 代碼之一,但正像 Maarten 發現的那樣,Windows 對表徵圖模式和縮圖模式都返回 LV_VIEW_ICON。
  那麼如何區分到底是哪種視圖模式呢?我絞盡腦汁並鑽進標頭檔尋找,最後發現一個叫 LVM_GETITEMSPACING 的訊息,該訊息是作什麼用的呢——用來擷取表徵圖間隔。顧名思義,表徵圖間隔是表徵圖視圖模式中表徵圖之間的像素間隔。LVM_GETITEMSPACING 不是很好使用,以至於 MFC 都沒有對之進行封裝(比如說 MFC 中並沒有 CListCtrl::GetIconSpacing 這樣的函數)。所以在 MFC 中你得自己發送訊息:

CSize sz = CSize(plc->SendMessage(LVM_GETITEMSPACING));

  Windows 按照通常方式返回尺寸,在高位和低位字中編碼的 cx/cy,然後CSize很禮貌地為你進行解碼。一旦有了表徵圖間隔,你便可以將它與 GetSystemMetrics(SM_CXICONSPACING) 返回的系統間隔值進行比較。如果列表視圖的表徵圖間隔與系統的一樣,則視圖是表徵圖模式。如果大於系統間隔,則視圖為縮圖模式:

if (sz.cx > GetSystemMetrics(SM_CXICONSPACING)) {    // thumbnail view} else {   // icon view }

  講了那麼多縮圖,接下來的問題是如何持續化不同使用者會話的檢視狀態?對此,當程式終止時,你需要用 Profile 函數在使用者設定檔中儲存最後使用的模式,並在下一次啟動程式時再次恢複它。我寫了一個小示範程式,DlgTest。程式使用了一個實現持續化程式行為的類 CPersistOpenDlg。這個類又藉助另外一個類 CListViewShellWnd,用它來封裝 SHELLDLL_DefView 視窗(參見三月份專欄)。CListViewShellWnd 包含擷取和設定視圖模式的函數,由這些函數來區分表徵圖和縮圖模式:

CListViewShellWnd m_wndLVSW;...m_wndLVSW.SetViewMode(ODM_VIEW_THUMBS);

  CListViewShellWnd 的 OnDestroy 處理器在某個資料成員 m_lastViewMode 中儲存視圖模式。當對話方塊被銷毀時,CPersistOpenDlg 的解構函式調用 WriteProfileInt 將這個值寫入使用者設定檔。對話方塊啟動時,CPersistOpenDlg 給自己送一個初始化訊息;該訊息處理常式調用 GetProfileInt 從磁碟讀取儲存在設定檔中的值並設定視圖模式。PostMessage 是必須調用的,因為常規初始化訊息 WM_INITDIALOG 和 CDN_INITDONE 在檔案對話方塊被完全初始化之前就會到來——有關這一點的解釋參見三月份專欄。
  順便說一下,任何時候你都應該使用 GetProfileXxx 和 WriteProfileXxx 來持續化應用程式的設定。MFC 用 CWinApp 封裝了這些函數。如果你在應用程式啟動時調用(一般都是在 InitInstance  函數中) CMyApp::SetRegistryKey("KeyName"),MFC 使用註冊表來儲存使用者配置資訊,而不是 INI 檔案。下面是 DlgTest 用的 INI 檔案:

 

[settings]ViewMode=28717

問題: 偶爾在一些文字資料和 C++ 文檔以及 Microsoft .NET 架構中看到術語“POD 類型”。這個術語是什麼意思?
Shelby Nagwitz 答案: 你可以將 POD 類型看作是一種來自外太空的用綠色保護層封裝的資料類型,POD 意為“Plain Old Data”(譯者:如果一定要譯成中文,那就叫“徹頭徹尾的老資料”怎麼樣!)這就是 POD 類型的含義。其確切定義相當粗糙(參見 C++ ISO 標準),其基本意思是 POD 類型包含與 C 相容的未經處理資料。例如,結構和整型是 POD 類型,但帶有建構函式或虛擬函數的類則不是。 POD 類型沒有虛擬函數,基類,使用者定義的建構函式,拷貝構造,賦值操作符或解構函式。
  為了將 POD 類型概念化,你可以通過拷貝其位元來拷貝它們。此外, POD 類型可以是非初始化的。例如:

struct RECT r; // value undefinedPOINT *ppoints = new POINT[100]; // dittoCString s; // calls ctor ==> not POD

  非 POD 類型通常需要初始化,不論是調用預設的建構函式(編譯器提供的)還是自己寫的建構函式。
  過去, POD 對於編寫編譯器或與C 相容的 C++ 程式的人來說很重要。現在,POD 來到 .NET 的環境中。在託管 C++ 中,託管類型(包括 __value 和 __gc 兩者)能包含嵌入的原生 POD 類型。 Figure 3 展示了例舉說明代碼。託管的 Circle 類能包含 POINT,但無法包含 CPoint 類。如果你嘗試編譯 pod.cpp 會報一個 C3633 錯誤:“Cannot define ''m_center'' as a member of managed ''Circle'' because of the presence of default constructor ''CPoint::CPoint'' on class ''CPoint''.”(譯者:意思是由於類 CPoint 有預設的建構函式‘CPoint::CPoint’,所以不能將‘m_center’定義為託管類‘Circle’的一個成員)
  .NET 限定嵌入的本機物件只能為 POD 類型的理由是這樣做能安全地拷貝它們,不用擔心調用建構函式,初始化虛表,或任何非 POD 類型需要的其它機制。

向 Paul 提問和評論請發到 cppqa@microsoft.com.
  作者簡介
  Paul DiLascia 是一名自由作家,顧問和 Web/UI 設計者。他是《Writing Reusable Windows Code in C++》書(Addison-Wesley, 1992)的作者。通過 http://www.dilascia.com 可以獲得更多瞭解。   本文出自 MSDN Magazine 的 November 2004 期刊,可通過當地報攤獲得,或者最好是 訂閱
本文由 VCKBASE MTT 翻譯

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.