目录
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
void Swap(char& left, char& right)
{
char temp = left;
left = right;
right = temp;
}
以前,我们如果要实现Swap函数,需要实现三个不同类型参数的函数,我们需要重载很多的同名不同参数类型的重复函数,如上图,会很麻烦,且会使我们的代码变得臃肿。而C++,提供了了函数模版的概念,很好的解决了我们这个问题,这也叫泛型编程。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
1. 函数模板
概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
格式
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
示例如下
//示例
template<typename T>
void Swap(T a1, T a2)
{
T tmp = a1;
a1 = a2;
a2 = tmp;
}
template<class T> //typename 和 class 在这里没有区别都可以用
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
Add(1,2);
Add(1.1,2.2); //编译可以通过
return 0;
}
原理
这其实就相当于让编译器自动去推演我们需要一个怎样的参数函数,把我们该做的丢给编译器去完成。
当然,我们也可以告诉编译器我们需要一个怎样的参数类型函数,我们称之为显式实例化。
//显式实例化
Add<double>(1,2);//告诉编译器让T类型变成double
Add<int>(1.1,2.2); //告诉编译器让T类型变成int
//示例
template<typename T>
void Swap(T a1, T a2)
{
T tmp = a1;
a1 = a2;
a2 = tmp;
}
template<class T> //typename 和 class 在这里没有区别都可以用
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
Add<double>(1,2);//告诉编译器让T类型变成double
Add<int>(1.1,2.2); //告诉编译器让T类型变成int
return 0;
}
输出结果如下图
看上面的代码,我们其实可以提出一个疑问
※他们调用的是否是同一个地址的函数?
并不是,编译器会根据推演参数不同会去自动生成两个函数,跟你自己写是一样的,只不过是丢给编译器去做了。
让我们进入反汇编来证明。
我们可以在反汇编很清楚的看到 它们调用了两个不同地址的函数,说明不是同一个函数。
注意事项(重要)
1.模版的全缺省
函数的参数可以全缺省,模版的类型也可以进行全缺省,只不过区别是一个是赋值全缺省,一个是类型全缺省。
//示例
template<class T = int>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
Add<>(1,2); //如果想使用全缺省,就必须要这样写,这其实也是一种显式实例化
return 0;
}
2.模版不支持分离编译
模版不支持分离编译 即 声明和定义不在同一个文件 比如 声明在.h 定义在.cpp 这是不允许的,编译会报错。
但是可以在同一个文件下声明定义分离,比如在一个.cpp文件下
其格式非常严格,如下
//示例
template<class T = int>
T Add(const T& left, const T& right); //声明
template<class T = int>
T Add<T>(const T& left, const T& right)//定义 格式需要注意这样写
{
return left + right;
}
int main()
{
Add<>(1,2); //如果想使用全缺省,就必须要这样写,这其实也是一种显式实例化
return 0;
}
扩展
让我们来实现用模版来实现一个栈的结构,可以自己下来先试试。
template<class T = int> //模版的全缺省格式
class Stack
{
public:
Stack(size_t capacity = 4)
:_a(nullptr)
, _top(0)
, _capacity(0)
{
_a = new T[capacity];
_capacity = capacity;
}
void Push(T x); //声明
void Pop()
{
if (_top > 0)
{
--_top;
}
}
~Stack()
{
delete[] _a;
_top = _capacity = 0;
}
const T& GetTop()
{
assert(_top > 0);
return _a[_top - 1];
}
bool isEmpty()
{
return _top == 0;
}
private:
T* _a;
size_t _top;
size_t _capacity;
};
template<class T>
void Stack<T>::Push(T x) //类模版类域指定要这样写
{
if (_top == _capacity)
{
_capacity = _capacity == 0 ? 4 : _capacity * 2;
T* tmp = new T[_capacity];
if (_a)
{
memcpy(tmp, _a, sizeof(T) * _top);
delete[] _a;
}
_a = tmp;
}
_a[_top] = x;
++_top;
}