目录
模板分为函数模板和类模板
在C++中使用模板可以让我们实现泛型编程
泛型编程
如果我们需要实现一个加法add函数,那么会怎么实现呢?
int Add(int a, int b)
{
return a + b;
}
上面就是一种实现方法
但是它有严重的缺点,就是它的返回值和形参都是固定的,都是整型
如果我们还要写浮点型,那么我们还得多写一个函数
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
但是在C++中我们有模板,我们可以理解为这个Add函数就是模具,我们给它什么材料它就是什么材料
所以如果是C++我们可以这样写
template<class T>
T Add(T a, T b)
{
return a + b;
}
这就是函数模板
函数模板
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时根据实参产生函数特定类型的版本
函数模板格式
上面的加法的模板就是一种简单的格式
template<class T>
T Add(T a, T b)
{
return a + b;
}
首先要有template这个关键字, 后面跟上<>里面的class也是一个关键字,T就是一个类似于类型的东西,我们实例化出来的其他内置类型最后就会把这个T替换掉
这是比较常用的一种写法
当然我们也可以把class换成typename
template<typename T>
T Add(T a, T b)
{
return a + b;
}
这两种写法效果都一样
也可以写多个模板
template<class T1, class T2, ..., class Tn>
void Add(T1 a, T2 b, ..., Tn n)
{
//...
}
函数模板的原理
在编译的时候,如果我们在一个main函数里既写了整型的Add又写了浮点型的Add,那么这是两个函数还是一个函数呢?
答案是两个函数
相当于编译器帮我们写了两个函数,只是单纯的把模板T给推演成了其他的内置类型,如下图
所以模板就相当于编译器帮我们做了我们原本要做的事情
当然了上面的Add函数其实写的都不是很好,我们不改变a和b的话可以加上const修饰,还可以加上引用减少拷贝,提高效率
template<class T> T Add(const T& a, const T& b) { return a + b; }
函数模板的实例化
如果类型不匹配是会报错的
template<class T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 10;
double d = 10.0;
Add(a, d);
return 0;
}
这里就是因为a和d的类型不一致导致报错
编译器识别出a是int类型,到模板里那个T就是int,但是后面的d是double,跟int匹配不上,所以自然就报错了
解决方案
template<class T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 10;
double d = 10.0;
Add(a, (int)d);
return 0;
}
可以像上面把d强转成int类型即可
template<class T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 10;
double d = 10.0;
Add((double)a, d);
return 0;
}
或者是把a强转成double类型
template<class T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 10;
double d = 10.0;
Add<int>(a, d);
return 0;
}
也可以在函数名后面加上<类型>,相当于是告诉了编译器我要实例化一个int类型的Add,这样即使d的类型不匹配,进入函数后也会隐式类型转换成int
template<class T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 10;
double d = 10.0;
Add<double>(a, d);
return 0;
}
当然double也没问题,和前面的int原理一致
类模板
之所以叫类模板就是在类中使用模板,
类模板格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
也就是在一个类中,我们可以使用多个类型
template<class T>
class Stack
{
public:
Stack(size_t capacity = 4)
{
_array = new T[capacity];
_capacity = capacity;
_size = 0;
}
void Push(const T& data);
private:
T* _array;
size_t _capacity;
size_t _size;
};
也就是说,例如我们写一个栈, 写了一个栈的模板出来,就相当于把所有内置类型的栈都写了出来,我们在函数内部想定义什么类型的栈就定义什么类型的栈,这是C语言办不到的
int main()
{
Stack<int> st1;
Stack<double> st2;
return 0;
}
类模板实例化
上面的代码就是一个实例化
我们实例化出了一个int类型的栈和double类型的栈
因为我们使用一个类的时候不像函数一样可以传参从而让编译器缺定我们模板的类型是什么
所以我们在实例化一个类的时候需要加上<类型>来让编译器知道我们需要什么类型的模板,从而给我们实例化出来,就像上面函数模板中的a和d一样
完