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

ATL 布幔之下的秘密 (3):模板元編程核心技術——策略模式、智慧指標與靜態多型

     編輯:關於VC++

前言:ATL 的靈魂是模板

ATL 大量運用 C++ 模板實現靜態多型、策略模式、智慧指標等核心機制,避免虛函數開銷。本文系統梳理 ATL 中關鍵的模板技術,並以可編譯範例逐一演示。

基礎:函數模板與類別模板

template <typename T>
T Max(const T& a, const T& b) { return a > b ? a : b; }

// 顯式指定模板參數
Max<int>(5, 'B'); // 回傳 66 ('B' 的 ASCII)
Max<char>(5, 'B'); // 回傳 'B'

// 類別模板:堆疊
template <typename T, int Size = 10>
class Stack {
    T data[Size];
    int top = 0;
public:
    void Push(T v) { data[top++] = v; }
    T Pop() { return data[--top]; }
};
Stack<int, 20> s; // 編譯期決定大小,無動態配置開銷

模板特化:針對特定型別客製化行為

// 主模板
template <typename T> T Max(T a, T b) { return a > b ? a : b; }

// 針對 char* 特化:比較字串長度而非指標值
template <> char* Max<char*>(char* a, char* b) {
    return strlen(a) > strlen(b) ? a : b;
}

// 類別模板特化
template <typename T> class Test { void F(T) { cout << "T version"; } };
template <> class Test<int> { void F(int) { cout << "int version"; } };
Test<char> t1; t1.F('A'); // T version
Test<int> t2;  t2.F(10); // int version

ATL 中 CComQIPtr<T> 即為 CComPtr<T> 的特化版本,提供 QueryInterface 自動化。

策略模式:以模板參數注入行為

struct Round1 { void Play() { cout << "Round1"; } };
struct Round2 { void Play() { cout << "Round2"; } };

template <typename Policy>
class Game {
    Policy policy;
public:
    void Play() { policy.Play(); }
};
Game<Round1> g1; g1.Play(); // Round1
Game<Round2> g2; g2.Play(); // Round2

ATL 的執行緒模型 (CComSingleThreadModel, CComMultiThreadModel) 即採此模式,編譯期決定同步策略,零虛函數開銷。

代理模式:智慧指標

template <typename T>
class SmartPtr {
    T* p;
public:
    SmartPtr(T* ptr = nullptr) : p(ptr) {}
    ~SmartPtr() { if (p) p->Release(); }
    T* operator->() { return p; }
    T& operator*()  { return *p; }
    // 複製/移動語意需正确实现 (參考 CComPtr)
};
// 使用
SmartPtr<IUnknown> sp(pUnk);
sp->AddRef(); // 透過 operator-> 轉發

ATL 提供 CComPtr (單一介面) 與 CComQIPtr (自動 QI 多介面),完整實作參考計數、複製語意、附屬/分離語意。

靜態多型:Curiously Recurring Template Pattern (CRTP)

template <typename Derived>
class Base {
public:
    void DoSomething() {
        static_cast<Derived*>(this)->Impl(); // 編譯期解析
    }
};
class Derived : public Base<Derived> {
public:
    void Impl() { cout << "Derived::Impl"; }
};
Derived d; d.DoSomething(); // 輸出 Derived::Impl,無虛函數表

ATL 大量使用 CRTP:

  • CComObject<T> 繼承自使用者定義的 T (class CComObject : public T),實現 COM 物件的建立/銷毀邏輯靜態綁定。
  • CWindowImpl<T> 透過 ProcessWindowMessage 靜態分派訊息。

CRTP 的限制與虛函數的不可替代性

因此 ATL 僅在效能關鍵、繼承深度固定、無需基類指標多型的場合使用 CRTP (如視窗訊息分派、COM 物件實作),介面層仍採純虛函數 (IUnknown)。

ATL 實戰:CComObject 與介面對應表

// 使用者定義的 COM 類別
class MyCom : public IMyInterface,
              public CComObjectRootEx<CComSingleThreadModel>,
              public CComCoClass<MyCom, &CLSID_MyCom> {
    BEGIN_COM_MAP(MyCom)
        COM_INTERFACE_ENTRY(IMyInterface)
    END_COM_MAP()
    // ...
};

// ATL 內部:CComObject<MyCom> 繼承自 MyCom
template <class T> class CComObject : public T {
    // 實作 IUnknown (AddRef/Release/QueryInterface)
    // 透過 T 的 COM_MAP 靜態查找介面
};

總結

模板是 ATL 達成「小、快」的關鍵:

  • 策略模式:編譯期注入執行緒模型、鎖定策略。
  • 智慧指標:RAII 管理 COM 介面指標,防止洩漏。
  • CRTP:靜態多型替代虛函數,消除 vtable 開銷。
  • 模板特化:針對特定介面 (IUnknown) 提供優化實作。

下一篇將探討 ATL 的虛函數表內部佈局、ATL_NO_VTABLE 優化與多重繼承下的 offsetofclass 計算。

本文配套源碼
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
特性CRTP (靜態多型)虛函數 (動態多型)
多層繼承中斷:MostDerived : Derived 會呼叫 Derived::Impl自然支援
基類指標容器不可:Base<D1>* 無法指向 D2可行:Base* 陣列存放不同衍生類
效能零開銷,內聯優化虛函數表查找、間接呼叫
程式碼膨脹每個衍生類產生獨立程式碼共享虛函數表