📋 个人简介
破防周(考试周)终于结束了,鄙人有幸存活了下来。上一次我们学习了C++中内存空间的分类,变量在内存中的存储位置以及C++中新引入的内存管理方式new和delete,今天我们来学习一下C++中泛型编程的典范:模板。
模板介绍
在此之前我们学习了C++中新引入的新语法:函数重载。函数重载可根据传入的参数类型来判断该使用哪一个函数。比如下面的Swap函数就可以根据参数类型来判断该调用哪一Swap函数。
void Swap(int &a, int &b)
{
int c = a;
a = b;
b = a;
}
void Swap(double &a, double &b)
{
double c = a;
a = b;
b = a;
}
....
虽然函数重载很方便,但是仍然有一些问题:
- 重载的函数仅仅是类型不同,代码复用率低,一旦出现新类型,就需要用户自己增加。
- 代码的可维护性低,一个重载出错可能导致所有的重载出错。
那么我们可不可以写一套所有类型通用的模具,就像印刷机的模板一样,当我们传入不同参数时,编译器会依照这个模板自动来生成对应的代码呢?
在C++中添加了:函数模板和类模板。他们便是我们实现上述懒人方法的关键。
函数模板
函数模板格式
函数模板格式如下:
template<typename T1,typename T2...>
返回值 函数名(参数列表)
{
}
- template是C++中的关键字,用来声明某个模块为模板。
- typename是用来定义模板参数关键字,也可使用class替代(不可使用struct)。
- T1,T2…为类型名可用其他字符进行替换。
在这里呢,我先写一个Swap,交换函数的模板,来看看实际该怎么用。
template<typename T>
void Swap(T&a,T&b)
{
T c = a;
a = b;
b = c;
}
函数模板原理
函数模板是一个印刷版,它本身并不是一个函数。当我们去使用这个模板的时候,编译器会根据我们的传递的参数的类型,在编译时自动去生成一个对应的函数来供我们使用。
比如下面的一个加法的模板:
当我们去调用这个模板函数时,使用的并不是它本身,而是编译器根据传入参数的类型而推演出来的下面两个函数。
模板示例化
使用不同的类型去使用函数模板时,称为函数模板实例化。模板示例化分为:隐式实例化和显式实例化。
1.隐式实例化
就是让编译器根据传递的参数类型自己去推演模板的类型。我们刚刚写的加法就是如此,我们没有指定T的类型,而是由编译器自己去推演。
2.显式实例化
我们自己在函数名后的<>指定模板参数的类型称为显式实例化。
在上面这段代码中我,如果我们不对函数进行显式实例化的化就会发生报错,因为a,b为两个不同类型的参数,编译器无法通过推演来判断T的类型,而我们使用显式实例化给T声明了类型,此时再调用Add函数时编译器会将T看做int型,而传入的非int型的b也会进行类型转换之后再传递给Add函数。
模板参数匹配原则
-
一个非模板函数可以和一个同名的模板函数同时存在,而且该函数模板还可以被实例化为这个非模板函数 int Add(int a, int b)
{
return a + b;
}
template<typename T>
T Add(T a, T b)
{
return a + b;
}
int main()
{
int a = 1, b = 2;
Add(a, b);
Add<int>(a, b);
return 0;
}
-
对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。 int Add(int a, int b)
{
return a + b;
}
template<typename T1, typename T2>
T1 Add(T1 a, T2 b)
{
return a + b;
}
int main()
{
Add(1, 2);
Add(1, 2.0);
return 0;
}
类模板
类模板格式
类模板格式如下:
template<class T1, class T2, class T3...>
class 类模板名
{
};
这里使用STL中的vector容器的部分作为示范,vector其实就是我们以前在用C语言实现的动态顺序表。
template <class T>
class vector
{
public:
vector(size_t capacity = 10)
: _pDate(new T[capacity]),
_size(0),
_capacity(capacity)
{
}
private:
T *_pDate;
size_t _size;
size_t _capacity;
};
我们可以通过改变T的类型来改变vector容器中存储元素的类型。
类模板实例化
类模板实例化与函数模板实例化不同,之前我们可以让编译器自己去推断参数类型,但是类模板无法这样做,必须由我们自己去声明类型
vector<int> s1;
vector<double> s2;
结语
欢迎各位参考与指导!!!
|