在 C++ 中,物件建構是「由內而外」分層完成的:
因此,在基底建構子中呼叫虛擬函數,解析至基底類別版本,而非最衍生類別。解構時順序相反:衍生解構子先執行,虛函數表回退至基底,基底解構子中呼叫虛擬函數同樣解析至基底版本。
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# 物件一創建即為最終型別,虛函數表在建構前已就緒。建構子/解構子(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));
CPersistOpenDlgSHELLDLL_DefView 視窗操作於 CListViewShellWndOnDestroy) 以 WriteProfileInt 寫入使用者設定檔PostMessage 發送初始化訊息,讀取 GetProfileInt 還原模式(WM_INITDIALOG 太早,對話方塊尚未完全建立)// 設定檔範例
[settings]
ViewMode=28717 // 對應 LV_VIEW_THUMBNAIL 等自訂常數
若呼叫 CWinApp::SetRegistryKey,MFC 自動改用登錄檔儲存。
POD (Plain Old Data):與 C 相容的純資料型別。ISO C++ 標準定義嚴格,核心特徵:
struct RECT r; // POD:未初始化,位元模式不定
POINT* pts = new POINT[100]; // POD:同樣未初始化
CString s; // 非 POD:有建構子
std::vector v; // 非 POD:有建構子
memcpy/memmove 可直接複製,無需呼叫建構子__value/__gc 類別只能內嵌 POD 原生型別。CPoint 有建構子,不能內嵌於 Managed 類別;POINT 則可CLR 要求內嵌原生物件必須為 POD,確保:
stackalloc、fixed 等高效操作LVM_GETITEMSPACING 識別縮圖模式,配合 PostMessage 延遲初始化