template模板初步认识
1. 交换两个值引出模板(泛型编程)
针对所有广泛应用类型
#include <iostream>
using namespace std;
void Swap(int& left, int& right)
{
int tep = left;
left = right;
right = tep;
}
void Swap(double& left, double& right)
{
double tep = left;
left = right;
right = tep;
}
void Swap(char& left, char& right)
{
char tep = left;
left = right;
right = tep;
}
int main()
{
int a = 1, b = 2;
double c = 1.5, d = 2.5;
Swap(a, b);
Swap(c, d);
cout << a << " " << b << endl;
cout << c << " " << d << endl;
return 0;
}
这时的交换程序为分批交换的,当交换的变量都为整型时,调用整型函数,为双精度浮点时调用浮点函数,但是如果实参需要混合交换,比如又有整型又有浮点型,这时就需要我们重新规划写一个函数。
使用函数重载虽然都可以实现,但是也有不良的地:
1.重载的函数仅仅是类型不同,代码的复用率比较低,只要有新的类型出现就需要自己添加对应的函数。
2. 代码的可维护性比较低,一个出错可能所有重载都出错。
针对以上问题c++提出了模板
2.模板
2.1函数模板
函数模板代表了一个整体,函数模板没有类型,在使用时,它会被参数化,然后根据实参类型产生函数特定的类型函数版本。
模板格式:template<typename T>
或 template<class T>
,在这里 class 是不能代替 struct 的
template<typename T>
//template<class T>
//typename后面是类型名字,是可以随便起的
//T代表一个模板类型(虚拟类型)
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
int main()
{
int a = 1, b = 2;
double c = 1.5, d = 2.5;
Swap(a, b);
Swap(c, d);
cout << a << " " << b << endl;
cout << c << " " << d << endl;
return 0;
}
函数模板是一个蓝图,它本身并不是函数,是编译通过使用方式产生特定具体类型函数的摸具。所以起始模板就是将本来应该我们做的重复的事情交给了编译器。
当我们调用Swap函数时,因为使用的模板,类型都是未知的,这时,这个工作编译器就会帮我们解决,当编译器分析完后,就会进入函数进行Swap函数。
函数模板的实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化
函数模板实例化分为两种类型
1.隐式实例化:让编译器根据实参推演模板参数的实际类型
2. 显式实例化:在函数后的<>
中指定参数的实际类型
template<typename T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 1, b = 2;
double c = 1.4, d = 2.9;
//隐式实例化
cout << Add(a, b) << endl;
cout << Add(c, d) << endl;
//这两个并不是调用的同一个函数,而是根据实际提供的参数,然后实例化,生成它对应的函数
//显式实例化
//一个int型,一个浮点型,T只能转换一种类型,所以只能显式实例化
cout << Add<int>(b, d) << endl;
return 0;
}
这两个并不是调用的同一个函数,而是根据实际提供的参数,然后实例化,生成它对应的函数
显式实例化也是有用处的例如:
template<typename T>
T* Alloc(int n)
{
return new T[n];
}
int main()
{
double* p1 = Alloc<double>(10);
return 0;
}
函数模板swap在C++中库里头也有,我们也可以直接调用
int main() {
int x = 10, y = 20; // x:10 y:20
swap(x, y); // x:20 y:10
int foo[4]; // foo: ? ? ? ?
int bar[] = { 10,20,30,40 }; // foo: ? ? ? ? bar: 10 20 30 40
swap(foo, bar); // foo: 10 20 30 40 bar: ? ? ? ?
cout << "foo contains:";
for (int i : foo) cout << ' ' << i;
cout << '\n';
return 0;
}
出现函数名一样的情况
int Add(const int& a, const int& b)
{
return a + b;
}
template<typename T>
T Add(const T& a, const T& b)
{
return a + b;
}
int main()
{
int a = 1, b = 2;
double c = 1.4, d = 2.9;
//隐式实例化
cout << Add(a, b) << endl;
cout << Add(c, d) << endl;
return 0;
}
上面的代码我们可以看到,有两个Add函数,一个是模板函数,一个是定义的整型函数,调用函数时,如果存在匹配类型,就会优先使用匹配的类型,而不会使用模板类型,如果没有匹配类型则会调用模板函数。
2.2 类模板(都是显示实例化)
#include <iostream>
using namespace std;
#include <assert.h>
//类模板
template<class T>
class Stack
{
public:
Stack(size_t capacity = 4)
{
/*_array = (T*)malloc(sizeof(T) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}*/
//利用模板很明显的效率提高
if (capacity > 0)
{
_array = new T[capacity];//如果开取空间失败会抛出异常
_capacity = capacity;
_size = 0;
}
}
void Push(const T& x)
{
if (_capacity == _size)
{
_capacity = _capacity == 0 ? 4 : _capacity * 2;
if (_array == nullptr)
{
_array = new T[_capacity];
}
else
{
T* tmp = new T[_capacity];
memcpy(tmp, _array, sizeof(T) * _size);
delete[] _array;
_array = tmp;
delete[] tmp;
}
}
_array[_size++] = x;
}
void Pop()
{
assert(_size > 0);
--_size;
}
~Stack()
{
if (_array)
{
delete[] _array;
_capacity = 0;
_size = 0;
}
}
private:
T* _array = nullptr;
int _capacity = 0;
int _size = 0;
};
int main()
{
Stack<int> s1(0);
Stack<double> s2;
Stack<char> s3(100);
s1.Push(1);
s1.~Stack();
s2.~Stack();
s3.~Stack();
return 0;
}
类模板实例化和函数模板实例化是不同的,类模板实例化需要在类模板后面跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
Stack<int> s1;
Stack<double> s2;
Stack<char> s3;
2.3 模板的作用域
函数模板的作用域只在它归属下的这个函数里,如果除了函数在使用就失效了,而类模板的作用域只在它归属下的这个类里,出了类也就失效了
这时出了类,类模板就失效了,需要重新定义才可以
函数模板也是相同的