目录
1.泛型编程
2.函数模板?
2.1函数模板概念
?2.2函数模板实例化
2.21实例化分为两类:
2.22函数模板支持多个实例化参数?
2.3.模板参数的匹配问题?
2.31匹配原则
?3.类模板
3.1类模板格式与实例化
3.11格式:
4非类型模板参数
引言:C++为啥有模板类??
1:可用来创建动态增加或减少的数据结构
2:它与某种特定类型无关,因此代码可重复使用
3:它在编译时检查数据类型而不是运行时检查数据类型,保证了类型的安全
4:它是平台无关的,具有很好的移植性
1.泛型编程
引言:我们先来写一个交换函数
//交换int类型
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;
}
如果有字符型还需要来个char类型的重载,但是各位不觉得有些烦琐吗?而且调用的概率低的可怜,C++可是高级语言呢,那能不能改进一下呢?各位有没有见过模具,就比如做月饼等用的我们想让月饼啥样式就啥样式那种,C++也提供了一种解决方式---泛型编程
?模板是泛型编程的基础,使用反省编程可以编写与类型无关的通用代码,是代码复用的一种手段。
模板分为:
·类模板;
·函数模板
2.函数模板?
2.1函数模板概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
template<typename T1, typename T2,..., typename Tn >
返回值类型(一般是T) 函数名()
{
//...
}
?template是模板参数,T是模板类型,typename是关键字一般可以用class代替但是不能用struct代替。
?就比如这个代码,intdouble都能编译,所以推出这个模板是有用的。
这就是高级语言的的高级之处,将我们应该重复做的自己给做了。?
其实这个Swap函数我们也不用写,C++库里已经写了
?我们直接用就行了。
?2.2函数模板实例化
2.21实例化分为两类:
隐式实例化, 显式实例化。
隐式实例化就是让编译器根据实参推演模板参数的实际类型,如上述中的Swap函数。但是当我们传递的参数含有两种类型时,编译器无法推断T的具体类型,此时就需要我们使用显示实例化解决。
?解决方式1:我们手动将类型转换Add(a ,? (int)d );
解决方式2:显式实例化如下
显式实例化
??显示实例化:在函数名后面<>中指定参数类型。
? ? ? ? 如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。
2.22函数模板支持多个实例化参数?
template<class K, class V> //两个模板参数 void Func(const K& key, const V& value) { ?? ?cout << key << ":" << value << endl; } int main() { ?? ?Func(1, 1); //K和V均int ?? ?Func(1, 1.1);//K是int,V是double ?? ?Func<int, char>(1, 'A'); //多个模板参数也可指定显示实例化不同类型 }
2.3.模板参数的匹配问题?
2.31匹配原则
1:一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
//专题函数
int Add(int left, int right)
{
return left + right;
}
//模板函数
template<class T>
T Add(T left, T right)
{
return left + right;
}
int main()
{
Add(2, 3);
}
各位猜猜编译器会调用那个add函数?
看反汇编得知用的是专题Add,
专题Add函数就是专门用来解决加法的,而模板函数只是一个备胎,让我们选反正我选专题函数。
2:对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
?就比如这个代码,它与非模板函数不完全匹配并且调用模板函数的契合度更高,他就会调用模板函数。
3:模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
?3.类模板
3.1类模板格式与实例化
引言:我们都知道typedef int SLDate ,那我们的实例化对象有int型又有double型呢,一个typedef就管不了这么多了所以来了个类模板
3.11格式:
?template<class T1, class T2, ..., class Tn> class 类模板名 { ? ? // 类内成员定义 };
让我们用类模板写一个栈试试
//类模板完成栈
template<class T>
class Stack
{
public:
Stack(size_t capacity = 0)
:_a(nullptr)
, _top(0)
, _capacity(capacity)
{
if (capacity > 0)
{
_a = new T[capacity];
_top = 0;
_capacity = capacity;
}
}
~Stack();//类外定义
void Push(const T& x)
{
if (_capacity == _top)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
T* tmp = new T[newcapacity];
if (_a)
{
memcpy(tmp, _a, _top * sizeof(T));
delete[] _a;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
void Pop()
{
assert(_top > 0);
_top--;
}
const T& Top()
{
assert(_top > 0);
return _a[_top - 1];
}
bool Empty()
{
return _top == 0;
}
private:
T* _a;
size_t _top;
size_t _capacity;
};
//类外定义--加模板参数列表
template<class T>
Stack<T>::~Stack()
{
delete[] _a;
_top = _capacity = 0;
_a = nullptr;
}
int main()
{
Stack<int> s;//使用时需要显示提供参数类型
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
s.Push(5);
while (!s.Empty())
{
cout << s.Top() << " ";
s.Pop();
}
cout << endl;
return 0;
}
注意:类模板一定要指定,函数模板根据情况指定。如果模板函数不能推演就要指定指定模板参数进行显式实例化。
4非类型模板参数
它的名字有点绕,就是上面的类模板参数是泛型的根据需要能变成int,double,char等,但是这个非类模板参数就是这个参数当做一个常量来使用,比如我可以用它来指定一个数组的大小,数组大小就由我们随意改变即可。
template<class T,size_t N = 10>
class Mystack
{
public:
//...
private:
T _a[N];
size_t _top;
};
int main()
{
Mystack<int,100> st1; //100
Mystack<int,200> st2; //200
Mystack<int> st3; //可以给缺省参数:从右向左确,且连续
return 0;
}
这个n就是非类模板参数。
|