什么是模板
模板分为类模板和函数模板,关键字为template,基本的声明形式如下:
template<class T>;
//也可以写成这样
template<typename T>
class 和 typename 在声明模板参数时的用法是相似的,一般情况下可以互换
但在成员模板内部访问嵌套类型时,需要使用 typename。
下面举一个例子加以理解:
template <class T>
class MyClass {
public:
typename T::SomeType member;
// 使用typename来指明T::SomeType是一个类型
};
模板的原理
模板的原理就是通过所传递的参数类型进行实例化,推导出参数的具体类型,实例化分为显式实例化和隐式实例化。
隐式实例化
当我们不指定参数的类型时,编译器会根据所传递的参数类型进行隐式的实例化,并推导出参数的具体类型。
#include <iostream>
template<class T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main(void) {
int a = 10;
int b = 20;
swap(a, b);
std::cout << "a = " << a << ' ' << "b = " << b << std::endl;
swap(a, b);
std::cout << "a = " << a << ' ' << "b = " << b << std::endl;
return 0;
}
这里我们并没有显式的指定参数的类型,所以编译器根据传递的参数自动进行了隐式实例化,并推导出了参数的具体类型。
然而,当我们传递的参数类型不统一时,编译器就无法进行隐式实例化,会导致程序报错。
#include <iostream>
template<class T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main(void) {
int a = 10;
double b = 20.1;
swap(a, b); //double和int类型不统一,隐式实例化失败
std::cout << "a = " << a << ' ' << "b = " << b << std::endl;
swap(a, b);
std::cout << "a = " << a << ' ' << "b = " << b << std::endl;
return 0;
}
显式实例化
所以,针对以上的情况,我们可以显式的声明参数类型,告诉编译器进行显式的实例化,从而无需对参数类型进行推导。
#include <iostream>
template<class T>
T Add(const T& a, const T& b) {
return a + b;
}
int main(void) {
int a = 10;
double b = 10.2;
std::cout << Add<int>(a, b) << std::endl;
//对Add函数进行了显式的实例化,将参数类型显式的定义为int
//此时结果为20
return 0;
}
模板有两种类型,分别是函数模板和类模板,下面分别加以介绍:
函数模板
函数模板较为简单,首先声明模板关键字template,随后实现函数的定义,当我们传入实参时,模板会根据参数类型进行实例化,从而实现模板函数功能。
template<class T>
void swap(T& a, T& b) { //引用传参
T temp = a;
a = b;
b = temp;
}
类模板
类似于函数模板,只不过这里时声明成员参数为模板类型,
下面我将实现一个简单的vector容器加以说明:
这里的vector是一个动态容器,所以要对容器进行动态扩容
#include <iostream>
template<class T>
class vector {
public:
vector(){ //构造函数初始化
arr = nullptr;
_size = 0;
_capacity = 0;
}
~vector() { //析构函数,对容器进行释放操作
delete []arr;
arr = nullptr;
}
void push_back(const T& target) {
if (_size == _capacity) {
// 判断,并进行扩容
size_t n_capacity = _capacity == 0 ? 2 : _capacity * 2;
T* temp = new T[n_capacity]; //在堆上重新分配内存
if (arr) {
memcpy(temp, arr, sizeof(T) * _size); //拷贝原数据
delete[]arr;
arr = nullptr;
//可以省略,下面的代码重置了arr的指向,所以不会出现悬空指针
}
arr = temp;
_capacity = n_capacity;
}
arr[_size++] = target;
}
T operator[](size_t i) {
return arr[i];
}
private:
T* arr;
size_t _size;
size_t _capacity;
};
int main(void) {
vector<int> vec;
vec.push_back(1);
std::cout << vec[0] << std::endl;
return 0;
}