一)模板的概念
C++ 模板是一种泛型编程的工具,它允许程序员编写与类型无关的代码。简单来说,模板可以看作是一个代码生成器,能够根据用户提供的类型(或其他参数)自动生成具体类型的代码。这样可以提高代码的复用性,避免为不同类型编写重复的函数或类。
例如,我们想写一个函数来交换两个变量的值。如果没有模板,可能需要为不同类型(如int、double、char等)分别编写交换函数。但有了模板,就可以编写一个通用的交换函数,适用于多种类型。
二)函数模板
1)定义和语法:
函数模板以关键字template开头,后面跟着一个模板参数列表,放在尖括号<>中。模板参数通常是类型参数,用关键字typename或class来声明(在这种情况下它们的作用相同)。
例如,下面是一个简单的函数模板,用于交换两个变量的值:
template<typename T>
void swap(T& a, T& b)
{T temp = a;a = b;b = temp;
}
在这个例子中,T是一个类型参数。当调用swap函数时,编译器会根据传入的实际参数类型来确定T的具体类型,并生成相应的函数代码。
2)使用和实例化:
使用函数模板时,不需要显式地指定模板参数的类型。编译器会根据函数调用时传入的参数类型自动推断出模板参数的类型。
int main(){int a = 5, b = 10;swap(a, b); // 编译器会自动推断T为int类型double c = 3.14, d = 2.71;swap(c, d); // 编译器会自动推断T为double类型return 0;
}
当编译器遇到swap函数调用时,它会根据传入的参数类型进行模板实例化。对于int类型的参数,编译器会生成一个swap函数的实例,其中T被替换为int;对于double类型的参数,会生成另一个swap函数的实例,其中T被替换为double。
三)类模板
1)定义和语法:
类模板的定义与函数模板类似,也是以template关键字开头,后面跟着模板参数列表。
例如,下面是一个简单的类模板,表示一个动态大小的数组:
template<typename T, int size>
class Array
{
private:T elements[size];
public:T& operator[](int index) {return elements[index];}
};
在这个Array类模板中,有两个模板参数:T是元素的类型,int size是数组的大小。类模板内部可以像使用普通类型和变量一样使用这些模板参数。
2)使用和实例化:
使用类模板时,需要显式地指定模板参数的类型和值(如果模板参数是一个非类型参数,如上面例子中的size)。
int main()
{Array<int, 5> intArray; // 创建一个包含5个int元素的数组对象intArray[0] = 1;Array<double, 3> doubleArray; // 创建一个包含3个double元素的数组对象doubleArray[1] = 3.14;return 0;
}
这里通过指定int和5,以及double和3来分别实例化了两个不同的Array类对象。编译器会根据这些参数生成相应的类定义代码,就好像分别写了一个Array类用于int类型、大小为 5,和一个Array类用于double类型、大小为 3 一样。
四)模板的特化
概念:模板特化是指为特定的类型或一组类型提供特殊的模板实现。当使用这些特定类型调用模板时,编译器会选择特化的版本而不是通用的模板版本。这在某些情况下是很有用的,比如对于某些特殊类型,通用模板的实现可能效率低下或者不符合要求。
1)函数模板特化:
例如,对于上面的swap函数模板,当处理char*类型(字符串指针)时,我们可能希望它能交换字符串的内容,而不是指针的值。可以这样进行特化:
template<>
void swap<char*>(char*& a, char*& b)
{char* temp = a;a = b;b = temp;// 交换字符串内容char tempChar;int i = 0;while (a[i]!= '\0' && b[i]!= '\0') {tempChar = a[i];a[i] = b[i];b[i] = tempChar;i++;}
}
这里通过template<>来表示这是一个特化的函数模板,void swap<char*>(char*& a, char*& b)明确了这是针对char*类型的特化。
2)类模板特化:
对于类模板特化,同样可以为特定类型提供特殊的类定义。例如,对于Array类模板,当元素类型是bool时,我们可以提供一个更紧凑的存储方式(因为bool类型通常只需要 1 位来存储):
template<>
class Array<bool, 5>
{
private:unsigned char elements; // 使用一个字节来存储5个布尔值
public:bool& operator[](int index){return ((bool*)&elements)[index];}
};
这里对Array<bool, 5>进行了特化,采用了不同的内部存储方式和操作符重载的实现来适应bool类型的特点。