C++ 中的模板(Template)是泛型编程 的基石。它允许我们编写与类型无关的代码,即编写一个函数或一个类,让它们能够处理任何数据类型,而无需为每一种类型都重写一次代码。这大大提高了代码的可重用性 和灵活性 。
1. 什么是 template <typename T>? 这是一个模板声明 ,它告诉编译器接下来的代码(无论是函数还是类)是一个“蓝图”或“模板”,而不是一个具体的实现。
template: C++ 的关键字,表示这是一个模板。
<...>: 尖括号内是模板参数列表。
typename: 关键字,用来声明后面的标识符是一个类型参数。在模板参数列表中,typename 和 class 关键字是等价的,可以互换使用。但通常推荐使用 typename,因为它更明确地表示 T 是一个类型名称。
T: 这是一个模板参数 ,它是一个占位符 ,代表一个尚未确定 的数据类型。你可以使用任何合法的标识符来代替 T(例如 U, MyType 等),但 T 是最常用的惯例。
当你在代码中使用这个模板时(比如 max<int>(a, b)),编译器会根据你提供的具体类型(这里是 int),自动生成一个该类型的特定版本。这个过程被称为模板实例化 (Template Instantiation)。
2. 函数模板 (Function Templates) 函数模板允许你定义一个通用的函数,它可以接受任何类型的参数。
场景与示例 假设你需要一个函数来比较两个数的大小。使用函数模板,只需要一份代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> #include <string> template <typename T>T my_max (T a, T b) { return a > b ? a : b; } int main () { std::cout << "Max of 3 and 7 is: " << my_max (3 , 7 ) << std::endl; std::cout << "Max of 3.5 and 1.2 is: " << my_max (3.5 , 1.2 ) << std::endl; std::string s1 = "hello" ; std::string s2 = "world" ; std::cout << "Max of strings is: " << my_max (s1, s2) << std::endl; std::cout << "Max of 3 and 5.5 (as double): " << my_max <double >(3 , 5.5 ) << std::endl; return 0 ; }
工作原理 :编译器在编译时看到 my_max(3, 7),它知道参数是 int 类型,于是就会自动生成一个 int 版本的 my_max 函数。
3. 类模板 (Class Templates) 类模板允许你定义一个通用的类,其成员变量和成员函数可以处理任何数据类型。标准模板库(STL)中的容器,如 std::vector, std::list, std::map 等,都是类模板的经典例子。
场景与示例 假设你想创建一个可以存储任意类型数据的栈(Stack)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include <vector> #include <stdexcept> #include <string> template <typename T>class MyStack {private : std::vector<T> elements; public : void push (const T& element) { elements.push_back (element); } void pop () { if (elements.empty ()) { throw std::out_of_range ("Stack<T>::pop(): empty stack" ); } elements.pop_back (); } T top () const { if (elements.empty ()) { throw std::out_of_range ("Stack<T>::top(): empty stack" ); } return elements.back (); } bool isEmpty () const { return elements.empty (); } }; int main () { MyStack<int > intStack; intStack.push (10 ); intStack.push (20 ); std::cout << "Top of intStack: " << intStack.top () << std::endl; intStack.pop (); std::cout << "Top of intStack after pop: " << intStack.top () << std::endl; std::cout << "--------------------" << std::endl; MyStack<std::string> stringStack; stringStack.push ("Alice" ); stringStack.push ("Bob" ); std::cout << "Top of stringStack: " << stringStack.top () << std::endl; return 0 ; }
4. 模板的更多特性 多个模板参数 模板可以有多个类型或非类型参数。
1 2 3 4 5 6 7 8 9 10 11 template <typename T1, typename T2>class KeyValuePair {public : T1 key; T2 value; }; KeyValuePair<int , std::string> pair; pair.key = 1 ; pair.value = "Apple" ;
非类型模板参数 模板参数不仅可以是类型,还可以是整数、指针等编译期常量值。
1 2 3 4 5 6 7 8 9 10 template <typename T, int SIZE>class FixedArray {private : T arr[SIZE]; public : int getSize () const { return SIZE; } }; FixedArray<int , 100 > my_array;
模板特化 (Template Specialization) 当通用模板的实现对某个特定类型不适用或效率不高时,可以为该特定类型提供一个“特供版”的实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 template <typename T>class Printer {public : void print (T value) { std::cout << value << std::endl; } }; template <>class Printer <const char *> {public : void print (const char * value) { std::cout << "String: \"" << value << "\"" << std::endl; } }; Printer<int > p1; p1. print (123 ); Printer<const char *> p2; p2. print ("hello" );
总结
特性
描述
核心思想
代码复用 和 泛型编程 。编写与类型无关的代码。
工作原理
编译器根据你提供的具体类型,在编译时 自动生成相应版本的代码(模板实例化)。
template <typename T>
声明一个模板,T 是一个代表任意类型的占位符。
函数模板
创建通用函数。编译器通常能根据参数自动推导 类型。
类模板
创建通用类(如容器)。在使用时必须显式指定 类型。
优势
1. 代码简洁 :避免为不同类型编写重复代码。 2. 类型安全 :错误在编译期就能被发现,比使用 void* 等方式更安全。 3. 高性能 :因为实例化在编译期完成,没有运行时的额外开销。