文章目录
一、类模板基础概念
【1. 类模板:】
不能直接使用,必须实例化使用
【2. 类模板的实例化:】
用具体类型替换类模板类型参数
【3. 模板类:】
类模板实例化后生成模板类
【4. 模板名和类名:】
template<typename T>
class Node{};
- Node不是类名,是模板名称。
- Node才是类名,那么构造函数的名字应该为Node<T>(){};
在本类中使用类模板时,可以不加<T>,函数会自己给类模板名加上<T>,所以如果写为Node(){};也可以运行,但是在其他类中必须加上<T>,不然其他类根本不知道这个应该用什么类型实例化这个模板。
建议:除了本类的构造和析构以外,其他的地方都加上模板类型参数,如成员变量等。
【5. 在类外实现模板类的成员方法】
在类外实现模板类的成员方法,需要类作用域来标识属于哪个类。如,此时Node不是类名,而是模板名,类名是Node<T>,一个typename T的作用域是声明到类结束。所以需要重新声明,那么如:
template<typename T>
void Node<T>::show(){};
【6. 类模板不支持推演】
类模板不支持推演,只能明确给出类型。因为无法从传入的实参推演出类模板的类型。
【7. 类模板实例化是一个选择性实例化】
类模板实例化是一个选择性实例化,即调用该函数才实例化该函数。
如有成员函数show(),eat(),在show()中写错误代码,只要你在主函数中不调用它,就不会报错,因为没有编译show函数。所以,当存在模板时,程序运行成功不代表函数的实现都是成功的,可能只是你没有调用它,系统没编译,所以未发现错误。故写完所有函数,需要都调用一遍进行测试。
【8. 模板类型参数列表】
存放类型,非类型,模板。如:
template <typename T, template<typename E> class MLink=Link>//第二个为模板类
【9. typename关键字】
- 定义模板类型参数
- 声明一个类型
【10. 拷贝构造函数的模板会退化成构造函数。】
二、异常处理机制
异常处理机制,处理函数错误的情况:
- throw抛出异常信息,把异常信息放在一个对象里面抛出。
- try包含可能出现异常的代码块,从出现异常的代码直接跳转到catch,不会再执行后面的代码。
- catch捕获并处理异常
使用
try
{
函数
}
catch(std::exception& err)//捕捉错误异常
{
处理异常
}
来捕捉异常,并处理异常。如果不提供捕捉处理异常操作,那么程序碰到异常情况就会终止,如:
if(empty())
{
throw std::exception("stack empty!");
}
程序就会直接终止。
三、类模板的特例化
(一)概念
- 完全特例化,全特化;针对一个类型 ,类模板,函数模板都可以实现
- 部分特例化,偏特化;针对一部分类型,只有类模板可以实现。
(二)实现两个数相加模板类,依据其讲解下面概念
template <typename T1,typename T2>
class Sum
{
public:
Sum(T1 a,T2 b):ma(a),mb(b)
{
}
T1 add()//普通函数
{
std::cout<<"Sum(T1,T2)"<<std::endl;
return ma+mb;
}
private:
T1 ma;
T2 mb;
};
- 现在如果要实现两个字符串相加,对于char*类型的处理应该采取完全特例化的方式,通过类模板或函数模板实现。
- 如果要实现传入两个任意类型数值的地址,进行相加,那么就是对这一类情况的处理,因为可以是int*地址,double*地址,这时候只能实现部分特例化,通过类模板实现。
(二)完全特例化
完全特例化,可以通过类模板和函数模板特例化两种方式实现:
- 如果少数函数不能满足某个类型需求时,进行函数模板完全特例化,实现某些函数的特例化。
- 如果多数函数不能满足某个类型需求时,实现类模板的完全特例化版本。
【1.类模板完全特例化】
类模板完全特例化
- 重新写一个类,不需要模板类型参数,直接给出类型即可。
- 类中所有函数的类型都用char* 类型替换。
那么类模板完全特例化的代码如下:
template <>
class Sum<char*,char*>
{
public:
Sum(char* a,char* b):ma(a),mb(b)
{
}
char* add()
{
std::cout<<"Sum(char*,char*)"<<std::endl;
char temp[100]={
0};
strcpy_s(temp,strlen(ma)+1,ma);
strcat(temp,mb);
return temp;//返回局部变量,结果会丢失
}
private:
char* ma;
char* mb;
};
现在就有两个类存在,尝试进行调用:
int main()
{
Sum<int,int> s1(10,20);
s1.add();
Sum<char*,char*> s2("a","b");
s2.add();
}
实现完全特例化类模板成功。
【2. 类模板函数成员完全特例化】
类模板完全特例化,需要将全部的类进行重写,如果成员方法很多,那么代码冗余过大。我们可以对特定类型实现函数完全特例化,这样就只需要多实现一个函数,但是注意:
- 函数模板特例化的前提是必须存在函数模板,现在类中只有普通函数,所以我们首先要提供模板版本。
- 对函数模板进行char*类型的实例化,直接在原来的类中提供。
那么完整的代码为:
template <typename T1,typename T2>
class Sum
{
public:
Sum(T1 a,T2 b):ma(a),mb(b)
{
}
T1 add()//普通函数
{
std::cout<<"Sum(T1,T2)"<<std::endl;
return ma+mb;
}
//实现字符串相加函数模板特例化,需要先有模板
template <typename S1,typename S2>
S1 add()
{
std::cout<<"add<T>"<<std::endl;
return ma+mb;
}
//特例化
template<>
char* add<char*,char*>()
{
std::cout<<"Sum<>"<<std::endl;
char temp[100]={
0};
strcpy_s(temp,strlen(ma)+1,ma);
strcat(temp,mb);
return temp;//返回局部变量,结果会丢失
}
private:
T1 ma;
T2 mb;
};
int main()
{
Sum<int,int> s1(10,20);
s1.add();
Sum<char*,char*> s3("c","b");
s3.add<char*,char*>();
}
运行结果:
(三)部分特例化
部分特例化即偏特化实现处理指针相加,这是一种类型,可以是int*,double*等,只能用类模板实现。
- 需要定义模板参数列表,因为此时具体的类型不确定。
- 需要在类名后面加上实例化类型,并不是具体类型,是模板参数列表的类型。处理指针相加,那就是T1*类型
- 类中所有用到类型的都用实例化类型替换。
此时共两个类,第一个类就是模板类,第二个是偏特化模板类。第一个类在上面写了,这里就不写了。
template <typename T1,typename T2>
class Sum
{
public:
Sum(T1 a,T2 b):ma(a),mb(b