目录
模板
这篇博客我们要说的是:泛型编程,函数模板,类模板
1. 泛型编程
我们先来看一段代码
void Swap_int(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void Swap_char(char &c, char &d)
{
char cur = c;
c = d;
d = cur;
}
我们可以看到上面两个交换函数, 除了参数的类型之外其它的逻辑是完全一样的,那么我们要是在一个程序中交换一种类
型就写一个对应类型的Swap函数,岂不是太繁琐了,那么我们能不能实现出一个通用的函数,当我们需要传哪个类型就传哪
个类型呢?
当然可以,函数重载就可以实现,但是函数重载代码的复用率太低,一旦有新的类型出现,就需要增加对应的函数,并
且代码的可维护性太低,一个出错可能会导致所有重载全部出错
所以我们有了更加好的方法, 就是模板,那么什么是模板呢,就相当于一个模型, 你往里面倒什么样的材料就给你出什
么材料的东西,并不需要每个材料都弄一个模具,这样太浪费了
所以我们就引入了一个概念叫做泛型编程,而模板就是泛型编程的基础
泛型编程概念:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
2. 函数模板
概念
函数模板代表了一类函数,该函数模板与类型无关,使用的时候才被参数化,根据传入的实参类型来确定特定类型的版本
格式
template<typename T1, typename T2.......typename Tn>
template <class T>
void Swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
注意: typename是用来定义模板参数关键字,也可以使用class, 但是不能用struct代替class
原理
在编译阶段, 对于函数模板, 编译器会根据传入的实参类型来推演生成对应的函数来供用户使用,当我们用int类型使用函数
模板时,编译器将T确定为int类型,然后产生一份专门处理int类型的代码,那么对于char和double也是一样的.
实例化
隐式实例化
让编译器根据实参推演模板参数的实际类型
template <class T> T Add(const T &a, const T &b) { return a + b; } int main() { int a = 10; int b = 2; Add(a, b); return 0; }
我们转到反汇编来看:
可以看到编译器通过参数类型推演生成了一个int类型对应的函数 , 因为我们传的是两个int类型的参数, 编译器可以自动识
别, 然后实例化出一个参数为int类型的函数, 这种实例化就叫做隐式实例化.
显式实例化
在函数名后的<>中指定模板参数的实际类型
当我们传入一个int和一个double的时候, 这个时候就出错了, 显示没有与函数模板Swap匹配的类型, 是因为我们传入了一个int和一个double, 编译器不知道到底按int类型生成还是按照double类型生成, 这个有两种方法 : 1.强转 2.显示实例化
1. 强转
template <class T> T Add(const T &a, const T &b) { return a + b; } int main() { int a = 10; double b = 0.2; Add(a, (int)b); return 0; }
2. 显式实例化
template <class T> T Add(const T &a, const T &b) { return a + b; } int main() { int a = 10; double b = 0.2; Add<int>(a, b); return 0;
如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错
模板参数的匹配原则
1. 非模板函数和同名的模板函数同时存在
template <class T> T Add(const T &a, const T &b) { cout << "模板函数" << endl; return a + b; } int Add(int &a, int &b) { cout << "非模板函数" << endl; return a + b; } int main() { int a = 10; int b = 2; Add(a, b); return 0; }
运行结果:
我们可以看到当我们现在已经有了匹配的非模板函数时, 会调用非模板函数, 现成的已经有了, 就会调用已经有了的这个.
2. 如果非模板函数的参数和实参的类型不一样, 只能去实例化
template <class T> T Add(const T &a, const T &b) { cout << "模板函数" << endl; return a + b; } int Add(int &a, int &b) { cout << "非模板函数" << endl; return a + b; } int main() { double a = 10; double b = 2; Add(a, b); return 0; }
3. 非模板函数的参数没有模板函数的更匹配, 会走模板函数
template <class T,class T1> bool fun(const T &a, const T1 &b) { cout << "模板函数" << endl; return a > b; } bool fun(const int &a, const int &b) { cout << "非模板函数" << endl; return a > b; } int main() { //a为int, b为double int a = 10; double b = 2; fun(a, b); return 0; }
运行结果:
虽然非模板函数也可以运行, 但是毕竟是相当于double转换为int, 精度不够, 编译器这个时候就会实例化一个更匹配函数.
4.如果存在非模板函数, 且类型匹配, 但是指定显示实例化, 则必须实例化
template <class T,class T1> bool fun(const T &a, const T1 &b) { cout << "模板函数" << endl; return a > b; } bool fun(const int &a, const int &b) { cout << "非模板函数" << endl; return a > b; } int main() { //a为int, b为double int a = 10; int b = 2; fun<int>(a, b); return 0; }
运行结果:
3.类模板
定义格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
举例
我们定义一个结构体, 现在我们可以看到, 编译器已经报错了, _data只能存放int类型的数据, 可是我们想存char怎么办, 那就是模板
template <class T>
struct Vector
{
T _data;
};
int main()
{
Vector<int> v1;
v1._data = 1;
Vector<char> v2;
v2._data = 'a';
cout << v1._data << endl;
cout << v2._data << endl;
return 0;
}
我们可以定义自己想要存放的类型
运行结果:
实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模
板名字不是真正的类,而实例化的结果才是真正的类。
比如上面的Vector<int>, 才是类型, Vector只是一个类名
模板实现动态顺序表
template <class T>
class Vector
{
public:
Vector(int capacity = 10)
:_data(new T[capacity])
{}
void push_back(const T& val)
{
//应该检查容量, 在这里就只实现简单逻辑了,主要是看看模板的使用
_data[_size++] = val;
}
void dispay()
{
for (size_t i = 0; i < _size; i++)
{
cout << _data[i] << ' ';
}
cout << endl;
}
private:
T* _data;
size_t _size;
size_t _capacity;
};
int main()
{
Vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.dispay();
Vector<char> v1;
v1.push_back('a');
v1.push_back('b');
v1.push_back('c');
v1.push_back('d');
v1.push_back('e');
v1.dispay();
return 0;
}
运行结果:
以上就是关于模板一些简单的知识和应用