首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ vs C#:建構子中呼叫虛擬函數、對話方塊視圖狀態持久化與 POD 型別深度解析

     編輯:關於C++

一、建構子/解構子中呼叫虛擬函數:C++ 與 C# 的根本差異

C++ 行為:靜態綁定至當前類別

在 C++ 中,物件建構是「由內而外」分層完成的:

  1. 基底類別建構子執行 → 虛函數表指向基底類別的虛函數表
  2. 衍生類別建構子執行 → 虛函數表更新為衍生類別的虛函數表

因此,在基底建構子中呼叫虛擬函數,解析至基底類別版本,而非最衍生類別。解構時順序相反:衍生解構子先執行,虛函數表回退至基底,基底解構子中呼叫虛擬函數同樣解析至基底版本。

class Base {
public:
    Base() { VirtualFunc(); }  // 呼叫 Base::VirtualFunc()
    virtual void VirtualFunc() { cout << "Base\n"; }
    virtual ~Base() { VirtualFunc(); } // 呼叫 Base::VirtualFunc()
};
class Derived : public Base {
public:
    Derived() { VirtualFunc(); }  // 呼叫 Derived::VirtualFunc()
    void VirtualFunc() override { cout << "Derived\n"; }
    ~Derived() { VirtualFunc(); } // 呼叫 Derived::VirtualFunc()
};

C# 行為:動態綁定至最衍生型別

C# 物件一創建即為最終型別,虛函數表在建構前已就緒。建構子/解構子(Finalizer)中呼叫虛擬方法總是解析至最衍生類別實作,即使衍生建構子尚未執行。

class Base {
    public Base() { VirtualMethod(); } // 呼叫 Derived.VirtualMethod()!
    public virtual void VirtualMethod() { Console.WriteLine("Base"); }
}
class Derived : Base {
    private int x = 10; // 尚未初始化!
    public Derived() { }
    public override void VirtualMethod() { Console.WriteLine("Derived: " + x); } // x=0 或拋出例外
}

危險:衍生類別欄位尚未初始化,虛擬方法若存取會導致邏輯錯誤或例外。

設計哲學差異根源

C# 採用「完整型別建構」主要為垃圾收集器服務:GC 需從型別快速推斷物件大小。若允許部分建構,同一 typeof 可能對應不同 sizeof,GC 掃描堆積時無法準確計算大小,將嚴重影響效能。C++ 無 GC,採用分層建構自然支援 RAII 與資源管理。

二、檔案開啟對話方塊視圖模式持久化

問題:如何識別並儲存「縮圖模式」?

CListCtrl::GetView() 對圖示模式與縮圖模式均返回 LV_VIEW_ICON。解決方案:查詢圖示間距——縮圖模式間距大於系統預設值。

HWND hList = ::FindWindowEx(hDlg, NULL, _T("SysListView32"), NULL);
CListCtrl* pList = (CListCtrl*)CWnd::FromHandle(hList);
CSize spacing = pList->SendMessage(LVM_GETITEMSPACING); // 取得圖示間距
bool isThumbnail = (spacing.cx > GetSystemMetrics(SM_CXICONSPACING));

持久化實作:CPersistOpenDlg

  • 封裝 SHELLDLL_DefView 視窗操作於 CListViewShellWnd
  • 對話方塊銷毀時 (OnDestroy) 以 WriteProfileInt 寫入使用者設定檔
  • 啟動時以 PostMessage 發送初始化訊息,讀取 GetProfileInt 還原模式(WM_INITDIALOG 太早,對話方塊尚未完全建立)
// 設定檔範例
[settings]
ViewMode=28717  // 對應 LV_VIEW_THUMBNAIL 等自訂常數

若呼叫 CWinApp::SetRegistryKey,MFC 自動改用登錄檔儲存。

三、POD 型別:Plain Old Data 完全解析

定義與特徵

POD (Plain Old Data):與 C 相容的純資料型別。ISO C++ 標準定義嚴格,核心特徵:

  • 無使用者定義建構子/解構子/拷貝/移動/賦值運算子
  • 無虛擬函數、無虛擬基底類別、無非靜態私有/保護成員
  • 所有非靜態成員均為 POD 型別

判斷範例


struct RECT r;           // POD:未初始化,位元模式不定
POINT* pts = new POINT[100]; // POD:同樣未初始化
CString s;               // 非 POD:有建構子
std::vector v;      // 非 POD:有建構子

POD 的關鍵意義

  1. 位元級拷貝安全:memcpy/memmove 可直接複製,無需呼叫建構子
  2. 可未初始化:效能敏感場合可避免零初始化開銷
  3. C 互操作:可安全跨語言邊界傳遞(如 C API、序列化)
  4. Managed C++ 限制:__value/__gc 類別只能內嵌 POD 原生型別。CPoint 有建構子,不能內嵌於 Managed 類別;POINT 則可

.NET 環境下的 POD

CLR 要求內嵌原生物件必須為 POD,確保:

  • GC 掃描時無需追蹤物件內部指標
  • AppDomain 邊界封送拷貝無需執行建構子/解構子
  • 記憶體配置單純,支援 stackalloc、fixed 等高效操作

總結

  • C++ 建構子中虛擬呼叫綁定靜態型別,C# 綁定動態型別——源自 GC 設計考量
  • 對話方塊視圖模式持久化關鍵在 LVM_GETITEMSPACING 識別縮圖模式,配合 PostMessage 延遲初始化
  • POD 是 C++ 與 C/.NET 互操作的基石,掌握其定義與限制對系統級開發至關重要
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved