进阶篇_STL详解(函数模板特化,类模板特化,用模板实现自己的通用算法)

本文详细探讨STL中的函数模板和类模板,讲解模板的实例化过程,包括隐式和显式实例化,并通过实例展示如何使用模板实现自己的通用算法,增强代码的复用性和灵活性。
摘要由CSDN通过智能技术生成

STL ~= 容器 + 算法 + 迭代器。容器用于容纳数据,算法用于处理数据,迭代器像胶水一样将容器和算法紧密地结合在一起。算法通过迭代器来定位和操控容器中的元素,事实上,每个容器都有自己的迭代器,只有容器自己才知道如何访问自己的元素,这使得迭代器就像一个指向容器中元素的普通指针一样。


#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//工资统计,计算收入大于1000的员工人数 

int main()
{
	vector<int> vecSalary;
	
	int put;
	do
	{
		cin>>put;
		if(0 == put)
			break;
		vecSalary.push_back(put);
	}while(true);

	int total = count_if(vecSalary.begin(), vecSalary.end(), bind2nd(greater<int>(), 1000)); //count_if:返回区间中满足指定条件的元素数目。          
	cout<<total<<endl; 
	
	return 0;
}
容器vector的使用不像数组那样固定分配内存,其可以根据用户的输入动态地增长,保证内存空间的合理利用,又可以让程序具有很大的可扩张性,处理很大范围内的数据。由此可得到STL的主要几个好处:封装很多常用的数据结构(容器),提供很多常用的通用算法(算法),调试程序更加安全和方便,支持跨平台(使用STL的C++代码可以轻松地移植到其他平台上)。模板是实现代码重用的一种重要机制,它可以实现类型的参数化,把类型定义为参数,而STL正是借助模板的威力建构起来的。

函数模板

如果说把函数比作一个箱子,每个箱子都是专用的(实现具体的不同功能),那函数模板就是一个万能箱子,它可以用来装各种东西,处理各种类型的数据。比如当编译器发现一个函数模板的调用后,将根据实参的实际数据类型来确认是否匹配函数模板中对应的形参,然后生成一个重载函数,称该重载函数为模板函数。根据参数类型的不同,一个函数模板可以随时变成各种不同的重载函数,从而实现对各种数据类型的处理。函数模板可以用来创建一个通用功能的函数,以支持各种不同的数据类型,简化重载函数的设计。函数模板的声明与使用如下:

#include <iostream>
using namespace std;
//模板函数范例
 
template <typename T>    //type理解为模板函数中的一个参数,代表抽象的数据类型 
T max(const T a, const T b)
{
    return a>b ? a : b;
}

int main()
{
    int a = 10;
    int b = 20;
    int c = std::max<int>(a, b);    //注意写法,不加std::,会error 
    cout<<c<<endl; //output: 20 
    
    float aa = 0.3;
    float bb = 0.4;
    float cc = std::max(aa, bb);    //max<float>的<...>不写编译器会自动分析生成相应的重载函数,但是有时候必须要指明类型参数以满足特殊需求,比如max<string> 
    cout<<cc<<endl; //output: 0.4 
    
    string aaa = "abcd";
    string bbb = "cde";
    string ccc = std::max(aaa, bbb); 
    cout<<ccc<<endl; //output: cde,这里输出cde,不是返回最长的而是返回字符较大的,所以如果需要应该对模板函数进行特化,实现特定类型的模板函数 
    
    return 0;
}

特化模板函数,实现特定类型的string模板函数,希望返回最长的字符串:

#include <iostream>
using namespace std;
//特化函数模板string范例

template <typename T>	//普通函数模板 
T max(const T a, const T b)
{
	return a>b ? a : b;
}

template <>	//特化string的函数模板 
string max<string>(const string a, const string b)	//注意这里及以上的写法格式 
{
	return a.size()>b.size() ? a : b; 
}

int main()
{
	int a = 5;
	int b = 4;
	float c = 5.3;
	float d = 4.4;
	cout<<std::max(a, b)<<endl<<std::max(c, d)<<endl;	//output: 5  5.3
	
	
	string x = "chen";
	string y = "aisda";
	cout<<std::max(x, y)<<endl; //用普通函数模板output: cde,返回字符较大的
	cout<<std::max<string>(x, y)<<endl; //用特化函数模板output: chen,返回字符串最长的 
	
	return 0;
}
类模板

#include <iostream>
using namespace std;
//特化类模板范例

//定义一个比较两个数的类模板compare<T> 
template <typename T>	//typename所定义的标识符实际上就是类模板的参数,模板参数可以是一个可以是多个 
class compare
{
public:
	compare(T a, T b)
	: m_a(a), m_b(b)
	{
	}
public:
	T min()
	{
		return m_a > m_b ? m_b : m_a;
	}
	T max()
	{
		return m_a > m_b ? m_a : m_b;
	}	
private:
	T m_a;
	T m_b;
};

int main()
{
	compare<int> intcompare(2, 3);
	cout<<intcompare.max()<<" > "<<intcompare.min()<<endl;
	
	compare<float> floatcompare(2.2, 4.4);
	cout<<floatcompare.max() <<" > "<<floatcompare.min()<<endl;

	return 0;
}
/*output: 3 >2  4.4 > 2.2*/ 

模板的实例化

模板的实例化分为:隐式实例化和显式实例化。

compare<int> intcompare(2, 3);	//隐式实例化
大多数都采用隐式实例化,具体的显式实例化如果需要时自行再查,注意的是显式实例化类时,所有的类成员也必须实例化。
用模板实现自己的通用算法

#include <iostream>
using namespace std;
/*软件谁集中,通常有一个撤销undo和恢复redo的通用功能*/

/*动作容器模板类,使用T作为模板参数*/
template <class T>
class actioncontainer
{
public:
	actioncontainer()
	{
		m_nredo = 0;
		m_nundo = 0;
	}
	void add(T value);	//向容器中添加新动作 
	T redo();	//恢复上一步动作 
	T undo();	//撤销上一步动作 
private:
	int m_nredo;
	int m_nundo;
	const static int ACTION_SIZE = 5;
	T m_redoAction[ACTION_SIZE]; //使用数组记录恢复和撤销的动作,数组元素的类型也是模板参数,它在类模板实例化时才确定 
	T m_undoAction[ACTION_SIZE];
};

/*模板类的成员函数实现*/
//向容器中添加动作,这里的T可以是一个基本数据类型,也可以是一个自定义类型 
template <class T>
void actioncontainer<T>::add(T value)
{
	//判断容器中的动作数目是否超过容器的容量 
	if(m_nundo >= ACTION_SIZE)
	{
		m_nundo = ACTION_SIZE - 1;
		for(int i=0; i<ACTION_SIZE; ++i)
			m_undoAction[i] = m_undoAction[i+1];	//将容器中已有的动作前移一个位置 
	}
	//将新动作添加到容器中
	m_undoAction[m_nundo++] = value; 
}

//撤销上一步动作 
template <class T>
T actioncontainer<T>::undo()
{
	m_redoAction[m_nredo++] = m_undoAction[--m_nundo];	//将撤销的动作复制到恢复数组中 
	return m_undoAction[m_nundo];	//返回撤销的动作 
}

//恢复上一步动作 
template <class T>
T actioncontainer<T>::redo()
{
	m_undoAction[m_nundo++] = m_redoAction[--m_nredo];	//将撤销的动作复制到恢复数组中 
	return m_redoAction[m_nredo];	//返回撤销的动作 
}

int main()
{
	actioncontainer<int> intaction;	//定义一个int类型的动作容器,这样这个动作容器就可以容纳int类型的数据 
	
	intaction.add(1);	//向容器中添加新的动作,也就是整数 
	intaction.add(2);
	intaction.add(3);
	intaction.add(4);
		 
	
	int nundo = intaction.undo(); //撤销上一步动作,这时,nundo的值为4
	nundo = intaction.undo();//再次撤销身上一步动作,nundo的值为3
	
	int nredo = intaction.redo(); //恢复上一步动作,这时,nredo的值为3
	nredo = intaction.redo();//再次恢复身上一步动作,nredo的值为4
	
	return 0;
}
 

以上代码定义了一个类模板actioncontainer<T>,这个类可以容纳动作(add)并且能够进行撤销(undo)和恢复(redo)动作。在这个模板类的内部,使用了两个数组来分别记录撤销和恢复的动作。这里的动作实际上是广义上的一种数据类型,它可以是一个基本数据类型比如int float,也可以是自己定义的数据类型,这样就使得这个动作容器具有广泛的通用性,可以处理各种数据类型。在这个动作容器类模板actioncontainer<T>中,还定义了它的成员函数,用于操作这个容器中的数据,其中包括添加新的动作到容器中、撤销或恢复动作等。当然,这里成员函数同样是对抽象的模板参数进行操作,其中并不涉及具体的数据类型,也就是说,这些操作与具体的数据类型无关。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值