面向物件三大支柱:封裝、繼承、多態。但 **模板** 常被視為「容器專用」,實則是 **靜態多態** 的利器,能在編譯期實現「介面注入」,讓風格、行為像積木一樣組合。本文以 **動畫彈出對話框** 為切入點,示範模板如何消除重複代碼,並延伸至狀態列、工具列的自動裝配。
// 傳統寫法:每個衍生類 OnInitDialog 都要重複
BOOL CMyDialog::OnInitDialog() {
CDialog::OnInitDialog();
AnimateWindow(1000, AW_CENTER); // 中心擴展動畫
return TRUE;
}
// 10 個對話框 = 10 次複製貼上,風格改動 = 10 處修改
template
class CAnimateDialog : public T {
public:
CAnimateDialog(UINT nIDTemplate, CWnd* pParent = NULL)
: T(nIDTemplate, pParent) {}
protected:
virtual BOOL OnInitDialog() {
BOOL bRet = T::OnInitDialog(); // 呼叫原始初始化
AnimateWindow(1000, AW_CENTER); // 注入動畫行為
return bRet;
}
};
// 原本聲明
CAboutDlg dlgAbout;
dlgAbout.DoModal();
// 改為:模板實例化
CAnimateDialog dlgAbout;
dlgAbout.DoModal(); // 自動擁有動畫效果!
// 同理,任何現有對話框類別瞬間獲得動畫能力
CAnimateDialog dlgLogin;
CAnimateDialog dlgSetting;
template
class CEnhancedDialog : public T {
CStatusBarCtrl m_wndStatusBar;
CToolBarCtrl m_wndToolBar;
public:
CEnhancedDialog(UINT nIDTemplate, CWnd* pParent = NULL)
: T(nIDTemplate, pParent) {}
protected:
virtual BOOL OnInitDialog() {
BOOL bRet = T::OnInitDialog();
// 1. 動畫
AnimateWindow(500, AW_SLIDE | AW_HOR_POSITIVE);
// 2. 狀態列 (底部)
m_wndStatusBar.Create(this);
m_wndStatusBar.SetIndicators(indicators, 2);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, reposQuery);
// ... 設定窗格文字
// 3. 工具列 (頂部)
m_wndToolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP, IDR_MAINFRAME);
m_wndToolBar.LoadToolBar(IDR_MAINFRAME);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0, reposQuery);
return bRet;
}
};
**效果**:所有套用 `CEnhancedDialog<OriginalDlg>` 的對話框,**自動擁有** 動畫、狀態列、工具列,且可透過 `m_wndStatusBar`/`m_wndToolBar` 直接操作。
| 技術 | 綁定時機 | 適用場景 | 代碼膨脹 |
|---|---|---|---|
| **繼承 (虛擬函式)** | 執行期 | 「是一種」關係、需動態多態、介面固定 | 無 (單一 vtable) |
| **組合 (成員物件)** | 編譯期 | 「有一個」關係、行為委派、低耦合 | 無 |
| **模板 (靜態多態)** | 編譯期 | **橫切關注點** (風格、日誌、序列化)、參數化演算法、CRTP | **每種具體類型產生一份代碼** |
template
concept DialogType = std::is_base_of_v;
template
using AnimatedDialog = CAnimateDialog; // 更清晰的語意
// 使用
AnimatedDialog dlg; // 編譯期檢查 T 必須繼承 CDialog
模板不是寫 `vector`/`map` 的專利。善用 **參數化基底類別**,你就能將「動畫風格」、「狀態列標配」、「工具列統一」、「右鍵選單統一」、「資料驗證規則」等 **橫切關注點** 封裝成可組合的 **Policy**,像樂高積木一樣賦予任何現有類別新能力——**不修改原始碼、不破壞繼承體系、編譯期零開銷**。這才是 C++ 「零開銷抽象」的迷人之處。