在 C++ 開發中,類成員的初始化方式主要有兩種:在建構函式主體內賦值,以及使用成員初始化列表。雖然兩者在許多情況下效果相同,但初始化列表在某些場景下是必要的,且通常更高效。
當成員物件本身是類別或結構,且只有帶參數的建構函式(沒有預設建構函式)時,必須使用初始化列表。例如:
class CMember {
public:
CMember(int x) { /* ... */ }
};
此時無法先建立 CMember 實例再賦值,必須在外層類別的建構函式中直接傳遞參數:
class CMyClass {
CMember m_member;
public:
CMyClass();
};
CMyClass::CMyClass() : m_member(2) { }
同理,const 成員與參考成員也只能在初始化列表中設定初值,因為它們不允許事後賦值。
以 MFC 的 CString 為例,若在建構函式主體賦值:
CMyClass::CMyClass() {
m_str = _T("yada yada"); // 先預設建構,再呼叫 operator=
}
編譯器會先生成 CString 的預設建構函式呼叫,再執行賦值運算子,共兩次函式呼叫。改用初始化列表:
CMyClass::CMyClass() : m_str(_T("yada yada")) { }
則直接呼叫帶參數的建構函式,僅一次呼叫。對於內建型別(int、long 等)兩者無性能差異,但養成使用初始化列表的習慣可提升程式碼一致性與可維護性。
成員初始化順序依宣告順序,而非初始化列表中的書寫順序。錯誤範例:
class CMyClass {
int m_x, m_y;
public:
CMyClass(int i) : m_y(i), m_x(m_y) { } // 危險:m_x 先被初始化,取得未定義的 m_y
};
解決方法:調整宣告順序,或確保初始化列表順序與宣告順序一致。
Windows 2000 引入了新版 commdlg.dll,提供帶有「位置欄」的開啟檔案對話框。MFC 應用若直接使用 CFileDialog,預設仍顯示舊版對話框,原因在於 MFC 內部使用 Hook 機制整合訊息對應,導致系統判斷為舊版應用而回退舊 UI。
OPENFILENAME 結構在 _WIN32_WINNT >= 0x0500 時新增三個欄位(pvReserved、dwReserved、FlagsEx)。lStructSize 判斷新舊版本,但 MFC 靜態連結或使用舊版 DLL 時,結構大小固定為舊版。OFN_ENABLEHOOK 標誌。若設定(MFC 預設會設定),則強制使用舊版對話框。繞過 CFileDialog,直接呼叫 GetOpenFileName 且不設定 Hook。範例:
void CMyApp::OnFileOpen() {
OPENFILENAME ofn = {0};
ofn.lStructSize = sizeof(ofn); // 使用舊版 SDK 編譯時為舊大小
ofn.hwndOwner = m_pMainWnd->GetSafeHwnd();
ofn.lpstrFilter = _T("所有檔案\0*.*\0");
ofn.nFilterIndex = 1;
ofn.lpstrFile = new TCHAR[MAX_PATH];
ofn.lpstrFile[0] = _T('\0');
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; // 不設 OFN_ENABLEHOOK
if (::GetOpenFileName(&ofn)) {
// 使用者選取檔案
}
delete[] ofn.lpstrFile;
}
此法在 Windows 9x/NT 顯示舊對話框,在 Windows 2000 以上自動顯示新版「位置欄」對話框,完美相容。
微軟後續版本的 Visual C++ 將提供支援新 UI 的 MFC DLL(不同檔名),屆時可直接使用 CFileDialog 享受新功能。在此之前,直接呼叫 API 是最穩健的選擇。