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

C++ 泛型編程進階:類模板實例化、容器適配器、仿函數與特化全解析

     編輯:關於C++

接續基礎篇,本文深入探討類模板實例化機制、成員函數特性、非類型參數實現容器適配器、STL 六大組件中的配接器與仿函數、模板特化(全特化/偏特化)及分離編譯方案。

一、模板編譯核心機制

核心原則:編譯器遇到模板定義不生成代碼,只有實例化時才生成具體代碼。這影響代碼組織方式與錯誤檢測時機。

為什麼模板定義要放頭文件

普通函數/類:聲明在 .h,定義在 .cpp,鏈接器解析符號。
模板不同:實例化需要看到完整定義 → 模板頭文件通常包含聲明與定義。

三階段編譯錯誤報告

  1. 編譯模板本身:語法錯誤(分號、拼寫)
  2. 遇到模板使用:實參個數、類型匹配(有限檢查)
  3. 模板實例化:類型相關錯誤(操作符缺失、成員不存在),可能延遲到鏈接時報告

二、類模板詳解

定義格式

template <class ...>  // 或 typename
class ClassName { ... };

動態順序表示例

template <typename T>
class SeqList {
public:
    SeqList() : _size(0), _capacity(10), _data(new T[_capacity]) {}
    ~SeqList() { delete[] _data; }
private:
    int _size, _capacity;
    T* _data;
};

void test() {
    SeqList<int> sl1;
    SeqList<double> sl2; // 編譯器生成兩個獨立類
}

類模板實例化特點

  • 編譯器不能推斷類模板參數,必須顯式提供 <Type>
  • 每種不同實參生成獨立的類,互不相關
  • 成員函數只有被使用時才實例化,允許部分成員對特定類型不適用

作用域內簡化模板名

template <typename T>
class SeqList {
    SeqList& operator++() { return *this; } // 類內可直接用 SeqList,無需 SeqList<T>
};

類外定義成員

SeqList<T>& SeqList<T>::operator++() { ... } // 類外需完整限定

三、非類型模板參數

template <class T, int N>
class SeqList { T a[N]; }; // 固定大小數組,N 為編譯時常量

四、模板參數實現容器適配器

模板參數可以是模板本身,這是 STL 容器適配器的基礎:

template <typename T>
class SeqList { ... }; // 底層容器

// 容器適配器:接受模板模板參數,提供缺省值
template <typename T, template <class> class Container = SeqList>
class Stack {
public:
    void Push(const T& x);
    void Pop();
    const T& Top();
    bool Empty();
private:
    Container<T> _con; // 使用傳入的容器模板實例化
};

void Test() {
    Stack<int> s1;           // 使用默認 SeqList
    Stack<int, SeqList> s2; // 顯式指定
}

五、STL 六大組件速覽

  1. 容器
  2. 算法
  3. 迭代器
  4. 空間配置器
  5. 配接器
  6. 仿函數

配接器分類

  • 容器配接器:stack、queue、priority_queue(底層默認 deque)
  • 迭代器配接器:reverse_iterator、insert_iterator、ostream_iterator 等
  • 仿函數配接器:bind1st、bind2nd、not1、not2 等(C++11 後用 std::bind/lambda 替代)

STL 容器適配器關係圖

六、仿函數

仿函數(Functor)是重載 operator() 的類對象,行為像函數,但可攜帶狀態、支持模板化。

冒泡排序遞增/遞減統一實現

class Greater {
public:
    bool operator()(int a, int b) { return a > b; }
};
class Less {
public:
    bool operator()(int a, int b) { return a < b; }
};

template <class Com = Greater>
void BubbleSort(int* a, int size) {
    Com com;
    for (int i = 0; i < size; ++i) {
        bool sorted = true;
        for (int j = 0; j < size - i - 1; ++j) {
            if (com(a[j], a[j+1])) { // 使用仿函數比較
                std::swap(a[j], a[j+1]);
                sorted = false;
            }
        }
        if (sorted) break;
    }
}

// 使用
BubbleSort<Less>(arr, n);  // 遞增
BubbleSort<Greater>(arr, n); // 遞減

七、類模板特化

全特化

template <typename T>
class SeqList { ... };

// 全特化 int 版本
template <>
class SeqList<int> {
public:
    SeqList(int capacity); // 可改變構造簽名
    ~SeqList();
private:
    int _size, _capacity;
    int* _data;
};

// 類外定義成員不加 template<>
SeqList<int>::SeqList(int capacity) : _size(0), _capacity(capacity), _data(new int[_capacity]) {
    std::cout << "SeqList<int>\n";
}

偏特化

針對模板參數進一步條件限制:

// 原始模板
template <typename T1, typename T2>
class Data { ... };

// 偏特化 1:固定 T2 為 int
template <typename T1>
class Data<T1, int> { ... };

// 偏特化 2:兩個參數均為指針
template <typename T1, typename T2>
class Data<T1*, T2*> { ... };

// 偏特化 3:兩個參數均為引用
template <typename T1, typename T2>
class Data<T1&, T2&> { ... };

使用示例:

Data<int, double> d1;      // 原始模板
Data<int, int> d2;         // 偏特化 1
Data<int*, double*> d3;   // 偏特化 2
Data<int&, double&> d4;   // 偏特化 3

八、模板分離編譯解決方案

分離編譯問題示意

推薦方案:將聲明與定義放入同一 .hpp 文件。
不推薦:頭文件顯式實例化 template class SeqList<int>;(編譯器支持度低、實例化依賴調用者)

總結

優點缺點
代碼復用、類型安全、性能零開銷、支撐 STL編譯緩慢、錯誤信息晦澀、代碼膨脹、調試困難
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved