本章深入探討 STL 核心思想——模板程式設計,並說明運算子多載如何與模板協作,支援泛型演算法。
模板提供參數化型別機制,分為函數模板與類別模板。函數模板類似預處理器巨集但具型別安全性。
#includeusing namespace std; // 傳統 int 版本 // int sum(int data[], int nSize) { ... } // 模板版本:型別參數化 template T sum(T data[], int nSize) { T total = 0; for (int i = 0; i < nSize; i++) total += data[i]; return total; } int main() { int a[] = {1, 2, 3, 4, 5}; double b[] = {1.1, 2.2, 3.3}; cout << "int 陣列總和: " << sum(a, 5) << endl; cout << "double 陣列總和: " << sum(b, 3) << endl; return 0; }
template <typename T> 中的 typename 可替換為 class,效果相同。編譯器會根據實際呼叫型別自動具現化函數。
模擬 std::vector 核心邏輯:動態擴充容量、記憶體管理封裝。
#includeusing namespace std; template class MyArray { private: int m_nTotalSize; // 總容量 int m_nValidSize; // 有效元素數 T* m_pData; // 資料指標 public: MyArray(int nSize = 2) : m_nTotalSize(nSize), m_nValidSize(0) { m_pData = new T[nSize]; } ~MyArray() { delete[] m_pData; } int GetValidSize() const { return m_nValidSize; } int GetTotalSize() const { return m_nTotalSize; } T Get(int pos) const { return m_pData[pos]; } void Add(T value) { if (m_nValidSize >= m_nTotalSize) { // 擴充為兩倍 T* pOld = m_pData; m_nTotalSize *= 2; m_pData = new T[m_nTotalSize]; for (int i = 0; i < m_nValidSize; i++) m_pData[i] = pOld[i]; delete[] pOld; } m_pData[m_nValidSize++] = value; } }; int main() { MyArray arr1; arr1.Add(10); arr1.Add(20); arr1.Add(30); // 觸發擴充 cout << "int 陣列: "; for (int i = 0; i < arr1.GetValidSize(); i++) cout << arr1.Get(i) << " "; cout << endl; MyArray arr2; arr2.Add(1.5); arr2.Add(2.5); cout << "double 陣列: "; for (int i = 0; i < arr2.GetValidSize(); i++) cout << arr2.Get(i) << " "; return 0; }
此類別展示了 STL 容器「記憶體分配、銷毀、再分配」的核心思想,將記憶體管理細節封裝,使用者無需關心底層操作。
STL 強調程式碼重用,traits 技術透過顯式模板特化,萃取不同型別的特性(如回傳型別、參數型別),提供統一介面。
有 IntArray 與 FloatArray 兩類別,其 Calculate() 回傳型別與參數型別分別為 int 與 float。欲撰寫統一介面 Array<T>::Calculate() 呼叫它們,但回傳/參數型別不同,難以直接處理。
// 步驟 1:基本模板類別(可為空) templatestruct NumTraits { }; // 步驟 2:模板特化,定義型別別名 template <> struct NumTraits { typedef int InputType; typedef int ResultType; }; template <> struct NumTraits { typedef float InputType; typedef float ResultType; }; // 步驟 3:統一介面類別使用 traits template class Array { public: typedef typename NumTraits ::ResultType ResultType; typedef typename NumTraits ::InputType InputType; ResultType Calculate(T& obj, InputType times) { return obj.Calculate(times); } };
typename 告訴編譯器 NumTraits<T>::ResultType 是型別而非靜態成員。當 T=IntArray 時,Calculate 簽名自動變為 int Calculate(IntArray&, int);當 T=FloatArray 時變為 float Calculate(FloatArray&, float)。達到型別安全的統一介面。
STL 演算法大量使用模板函數,常需比較、複製、指派等操作,因此自訂型別必須多載對應運算子。
#include#include using namespace std; template bool Greater(U const& u, V const& v) { return u > v; // 需要型別支援 operator> } class Student { string m_name; int m_grade; public: Student(string name, int grade) : m_name(name), m_grade(grade) {} // 多載 > 運算子,支援與 int 比較 bool operator>(int const& value) const { return m_grade > value; } }; int main() { Student s("Raito", 100); cout << boolalpha << Greater(s, 99) << endl; // true cout << Greater(2, 1) << endl; // true (內建型別) return 0; }
若 Student 未多載 operator>,Greater(s, 99) 將編譯失敗。模板函數提供演算法框架,運算子多載提供型別專屬的操作介面,兩者缺一不可。