【C++】十三、模板- - - -类模板

一、类模板基础概念

【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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值