C++模板学习
模板初阶
泛型编程
函数重载的几个不好的地方:
1.代码的复用率低,新类型出现时,就需要增加对应的函数。例如交换函数的书写时,就能体现出复用率低。
2.代码的可维护性比较低,一个出错可能让所有的重载均出错。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础。
模板又分为函数模板和类模板。
函数模板
概念:函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
typename是用来定义模板参数关键字,也可以使用class(不能使用struct代替class)
函数模板的原理
模板参数实例化分为:隐式实例化和显式实例化。
隐式实例化就是让编译器根据实参推演模板参数的实际类型,显式实例化就是在函数名后的<>中指定模板参数的实际类型。
注意隐式实例化中模板参数列表只有一个T,所以不能有多于一种的参数类型需要去推演。例如int,double两种类型.
template<class T>
T Add(const T& left,const T& right)
{
return left+right;
}
int main()
{
int a1=10,a2=20;
double d1=10.0,d2=20.0;
Add(a1,a2);
Add(d1,d2);
//Add(a1,d1);不能通过编译
Add(a1,(int)d1);
Add<int>(a1,d1);
return 0;
}
需要注意的点:
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例,如果模板可以产生一个具有更好匹配的函数,那么将选择模板。
3.模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。
类模板
类模板的定义格式
#include<iostream>
#include<cstdio>
#include<assert.h>
#include<algorithm>
using namespace std;
template<class T>
class Vector
{
public:
Vector(size_t m_capacity=10)
:pdata(new T[capacity])
,size(0)
,capacity(m_capacity)
{}//构造函数初始化
~Vector();//使用析构函数在类中声明,在类外定义
void PushBack(const T& data)
{
pdata[size++]=data;
}
void PopBack()
{
size--;
}
size_t Size()
{
return size;
}
T& operator[](size_t pos)
{
assert(pos<size);
return pdata[pos];
}
private:
T* pdata;
size_t size;
size_t capacity;
};
//类模板中函数放在类外进行定义时,需要加模板参数列表
template<class T>
Vector<T>::~Vector()
{
if(pdata)
{
delete[] pdata;
}
}
int main()
{
Vector<int> s1;
s1.PushBack(1);
s1.PushBack(2);
Vector<double> s2;
s2.PushBack(1.0);
s2.PushBack(2.0);
s2.PushBack(3.0);
for(size_t i=0;i<s1.Size();i++)
{
cout<<s1[i]<<" ";
}
cout<<endl;
for(size_t i=0;i<s2.Size();i++)
{
cout<<s2[i]<<" ";
}
cout<<endl;
return 0;
}
模板进阶
非类型模板参数
模板参数分类 :类型形参与非类型形参。
类型形参出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用另一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
****常量的类型只能是:整形(及相关类型),指针,引用。 浮点数类对象以及字符串不允许作为非类型模板参数。
****传入的时候必须是const类型的数据
模板的特化
模板的特化是指在原模板类的基础上,针对特殊类型所进行的特殊化的实现方式。模板特化分为函数模板特化和类模板特化。
函数模板特化步骤:
基础函数模板---->template后面接一对空的尖括号<> ------------>函数名后跟一对尖括号,尖括号中指定需要特化的类型
-------------------> 函数形参表必须要和模板函数的基础参数类型完全相同。
template<>
bool Isequal<char*>(char*& left,char*& right)
{
if(strcmp(left,right)>0)
{
return true;
}
return false;
}
类模板特化:
分为全特化和偏特化。
全特化就是将模板参数列表中所有的参数都确定化。
偏特化有以下两种表现方式:
**部分特化 将模板参数类表中的一部分参数特化
**参数更进一步的限制 偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
类模板特化应用之类型萃取
1.memcpy,strcpy均是浅拷贝。如果拷贝自定义类型对象,要确定自定义类型对象是浅拷贝还是深拷贝。深拷贝则不能使用memcpy,strcpy
2.使用循环复制的方式代码的效率比较低。
3.为了将内置类型与自定义类型区分开,可以采用类型萃取。
模板分离编译
经历步骤:
预处理---->编译---->汇编----->链接
模板总结
优点:
-
模板复用了代码,节省资源,更快的迭代开发
-
增强了代码的灵活性
-
进行完封装以后使用起来更加方便,维护性高
缺点:
- 导致代码膨胀问题,编译时间也会变长。
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误。
C++模板学习
模板初阶
泛型编程
函数重载的几个不好的地方:
1.代码的复用率低,新类型出现时,就需要增加对应的函数。例如交换函数的书写时,就能体现出复用率低。
2.代码的可维护性比较低,一个出错可能让所有的重载均出错。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础。
模板又分为函数模板和类模板。
函数模板
概念:函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
typename是用来定义模板参数关键字,也可以使用class(不能使用struct代替class)
函数模板的原理
模板参数实例化分为:隐式实例化和显式实例化。
隐式实例化就是让编译器根据实参推演模板参数的实际类型,显式实例化就是在函数名后的<>中指定模板参数的实际类型。
注意隐式实例化中模板参数列表只有一个T,所以不能有多于一种的参数类型需要去推演。例如int,double两种类型.
template<class T>
T Add(const T& left,const T& right)
{
return left+right;
}
int main()
{
int a1=10,a2=20;
double d1=10.0,d2=20.0;
Add(a1,a2);
Add(d1,d2);
//Add(a1,d1);不能通过编译
Add(a1,(int)d1);
Add<int>(a1,d1);
return 0;
}
需要注意的点:
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例,如果模板可以产生一个具有更好匹配的函数,那么将选择模板。
3.模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。
类模板
类模板的定义格式
#include<iostream>
#include<cstdio>
#include<assert.h>
#include<algorithm>
using namespace std;
template<class T>
class Vector
{
public:
Vector(size_t m_capacity=10)
:pdata(new T[capacity])
,size(0)
,capacity(m_capacity)
{}//构造函数初始化
~Vector();//使用析构函数在类中声明,在类外定义
void PushBack(const T& data)
{
pdata[size++]=data;
}
void PopBack()
{
size--;
}
size_t Size()
{
return size;
}
T& operator[](size_t pos)
{
assert(pos<size);
return pdata[pos];
}
private:
T* pdata;
size_t size;
size_t capacity;
};
//类模板中函数放在类外进行定义时,需要加模板参数列表
template<class T>
Vector<T>::~Vector()
{
if(pdata)
{
delete[] pdata;
}
}
int main()
{
Vector<int> s1;
s1.PushBack(1);
s1.PushBack(2);
Vector<double> s2;
s2.PushBack(1.0);
s2.PushBack(2.0);
s2.PushBack(3.0);
for(size_t i=0;i<s1.Size();i++)
{
cout<<s1[i]<<" ";
}
cout<<endl;
for(size_t i=0;i<s2.Size();i++)
{
cout<<s2[i]<<" ";
}
cout<<endl;
return 0;
}
模板进阶
非类型模板参数
模板参数分类 :类型形参与非类型形参。
类型形参出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用另一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
****常量的类型只能是:整形(及相关类型),指针,引用。 浮点数类对象以及字符串不允许作为非类型模板参数。
****传入的时候必须是const类型的数据
模板的特化
模板的特化是指在原模板类的基础上,针对特殊类型所进行的特殊化的实现方式。模板特化分为函数模板特化和类模板特化。
函数模板特化步骤:
基础函数模板---->template后面接一对空的尖括号<> ------------>函数名后跟一对尖括号,尖括号中指定需要特化的类型
-------------------> 函数形参表必须要和模板函数的基础参数类型完全相同。
template<>
bool Isequal<char*>(char*& left,char*& right)
{
if(strcmp(left,right)>0)
{
return true;
}
return false;
}
类模板特化:
分为全特化和偏特化。
全特化就是将模板参数列表中所有的参数都确定化。
偏特化有以下两种表现方式:
**部分特化 将模板参数类表中的一部分参数特化
**参数更进一步的限制 偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
类模板特化应用之类型萃取
1.memcpy,strcpy均是浅拷贝。如果拷贝自定义类型对象,要确定自定义类型对象是浅拷贝还是深拷贝。深拷贝则不能使用memcpy,strcpy
2.使用循环复制的方式代码的效率比较低。
3.为了将内置类型与自定义类型区分开,可以采用类型萃取。
模板分离编译
经历步骤:
预处理---->编译---->汇编----->链接
模板总结
优点:
-
模板复用了代码,节省资源,更快的迭代开发
-
增强了代码的灵活性
-
进行完封装以后使用起来更加方便,维护性高
缺点:
- 导致代码膨胀问题,编译时间也会变长。
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误。