C++程序设计案例教程(学习笔记)——Cha8 模板和STL

    模板是C++的一个语言特性,是实现参数多态的方法,也是C++实现泛型程序设计的重要机制。模板使程序设计使可以快速建立具有类型安全的类库和函数的集合。函数中的参数多态称为函数模板,类中的参数多态称为类模板,而STL标准模板库也正式成为标准C++库的一部分,提供了数据结构和算法。

  • 8.1 模板简介
        程序设计过程中会出现针对不同类的数据类型要进行完全相同的操作,通过模板这种机制将所定义的函数或者类中的部分数据的类型作为参数定义,在使用时通过实参来确定真正的类型。这就是参数化多态。
  • 8.2函数模板
int Max(int a, int b)
{
	return a>b?a:b;
}
int Max(double a,double b)
{
	return a>b?a:b;
}

将上述函数中的类型参数化就是将int 和double 用参数Type来替代,得到如下通用代码:

Type Max(Type a,Type b)

{
	return a>b?a:b;
}

当使用函数求两个整数或实数的最大值时,只需要将Type换成int或double即可。使用通用代码后,可使用的参数类型不仅限于int和double,可以是任意数值类型。

C++中使用关键字template定义模板,格式如下:

template<模板参数表>
函数定义

模板参数表中的内容为:

class 标识符 或 typename 标识符(至少1个)

当上述参数表包含多个参数时,各项之间用逗号隔开。标准C++建议使用typename。上述参数化的函数称为函数模板。函数模板不是函数,只是一个函数的样板。只有类型参数实例化后才会生成所要使用的函数。函数模板的使用和一般函数使用完全相同。

函数名(实参表)

例8.1函数模板的使用

include<iostream>
using namespace std;
template <typename T>
T Max(T a,T b)
{
	return  a>b?a:b;
}
int main()
{
	int i=1,j=2;
	double m=0.2,n=0.3;
	imax=Max(i,j);
	dmax=Max(m,n);
	cout<<"imax="<<imax;
	cout<<"dmax="<<dmax;
	rerturn 0;
}

  编译器编译上述程序时;会调用根据Max()所调用的实参推导出函数模板的类型参数,并使用推导所得的类型代替函数模板中的类型T。

  • 8.3类模板
    8.3.1类模板的定义
      类模板不是一个具体的类,而是类的一个模板。使用类模板可以生成类的类型,类模板实例化的到一个模板类。模板类才是真正的类,用它定义对象,然后使用对象的成员函数。
      类模板声明格式如下:
template<类模板参数>
类声明

其中类模板参数表中内容:

template <标识符> 或 class <标识符>  
或
<类型表达式>  <标识符>

    第一种<标识符>是参数化类型名,第二种<标识符>是则代表类声明所声明类中参数化的常量,<类型表达式> 规定了常量的类型。当模板参数表中同时包含上述多个参数时,参数之间用逗号隔开。
   类模板只有在使用时的时候才具体化为具体的类类型。模板类使用对象时,按如下形式声明:

类模板名<模板参数表> <对象名1>,...,<对象名n>

例:有越界检查功能的数组定义如下:

template<typename T>    //array.h
class Array<T>
{
	public:
		Array(int s);
		virtual ~Array();
		int Getsize()const;
		T& operator[](int);
	private:
	   int size;
	   T * element;
};
template<typename T>
Array<T>::Array(int s)
{
	size=s;
	element= new T[size];
}
template<typename T>
Array<T>::~Array()
{
	delete [ ] element;
}
template<typename T>
T & Array<T>::operator[ ] (int i)
{
	if(i<0||i>=size)
	{
		cout<<"下标越界"<<endl;
		exit(1);
	}
	return element[i];
}
说明:(1)类模板声明了一个参数化的类型T,用在成员和成员函数的声明中。
	 (2)因为这些函数都是类模板的成员函数,该类的模板名字是Array<T>,所以每个成员函数名前都加上Array<T>。

类模板的使用

#include<iostream>
#include"array.h"
using namespace std;
int main()
{
	Array <int>  a(5);
	int i;
	cin>>i;
	a[i]=1;
	cout<<a[i];
	return 0;
}

程序运行结果:

8
下标越界
说明:(1)使用类模板生成一个特定的类时,需要指定参数T所代表的类型。例如:Array <int>  声明了一个员类型为int的数组类。
     (2)编译器遇到类型表达式:Array <int>,通过将T替换成int生成所需要的类。

8.3.2 类模板用作函数的参数
类模板用作函数模板的参数使用

#include<iostream>
#include"array.h"
using namespace std;
template<typename T>
void F(Array <T> & t,int i)
{
	cout<<t[i]<<endl;
}
int main()
{
	Array <int>  a(10);
	int i;
	cin>>i;
	a[5]=i;
	F(a,5);
	return 0;
}

编译期间,编译器通过推导将F(a,5)由函数模板生成如下模板函数:

void F(Array < int >  &a,int i);

8.3.3 类模板用作基类
  对前面数组进行改进,使得数组下标不再是由“0”,而是有指定值开始。同时为了是定义数组不在受类型的限制,将数组实现为类模板,通过继承的方法可以实现需要的类模板。

#include"array.h"      //barray.h
template <typename T>
class bArray:public Array <T>
{
	public:
		bArray(int s,int b=0);
		T& operator[ ](int);
	private:
	    int min();
};
template <typename T>
bArray < T >:: bArray (int s,int b):Array<T>:Array<T>(s)
{
	min=b;	
}
template <typename T>
T & bArray <T>::operator [ ] (int i)
{
	return Array <T>::operator [ ]( i-min);
}
说明:(1)Array <T>是bArray < T >的基类,在bArray < T >构造函数的初始化列表中使用了表达式Array<T>(s)以调用基类的构造函数。
	(2) 为了实现基类成员函数使用了Array <T>::operator [ ]( i-min)
  • 8.4 STL
      C++标准模板库(STL)包含容器、算法和迭代字,容器包括链表、向量、对列、结合、映射等;算法模板包括排序、查找等各种算法;迭代子可以在不同容器上操作。
    8.4.1 STL简介
      STL容器是基于模板机制的,其中包括线性容器和非线性容器。主要容器有:vector(向量模板)、list(列表模板)、stack(栈模板)、queue(对列模板)、deque(双端对列模板)、map(映射模板)。
      迭代子可以方便访问STL中容器中的对象。STL中的迭代子可以看成指针的推广。迭代子有顺序访问和直接访问,分别对应顺序访问容器和直接访问容器。
      STL的算法是函数模板实现的,可以使用算法通过迭代子实现对不同类型对象的通用操作。算法与容器之间是通过迭代子进行沟通的,算法面向迭代子,迭代子面向容器。通过迭代子可以获得容器内部的数据对象,算法对这个由迭代子获得的对象进行操作。STL中的算法主要有:排线、查找、比较和统计。
    8.4.2容器
      容器是一种面向对象的数据结构表示的方法。C++中的数组可以看成一种内置的容器。C++提供了用户自己定义相关容器类的机制。
      容器类分为线性和非线性两大类。线性容器中元素是有序的,非线性容器中元素是无序的。
      以向量容器为例,向量可以对内部对象进行直接访问,也可以像链表一样对容器内部的对象进行顺序访问。因为向量具有动态特性,向量容器的大小可以动态增长。向量模板中重要成员函数模板如下:
    begin():返回指向向量第一个元素的迭代子。
    end():返回指向向量最后一个元素后位置的迭代子。
    push_back():是指在向量尾部添加元素。
    erase():删除向量任意位置上的元素。
    8.4.3迭代子
      可以看成指针的扩展,与指针类似,用于指向容器中的元素。迭代子对每种容器都有一个实现,作用在不同容器的迭代子也有不同类型,不同迭代子所支持的操作也不尽相同。但是间接引用运算符“ * ”直接应用一个迭代子,这样可以使用它所指向的元素。
      迭代子为访问容器中的元素提供了除指针之外的一种替代方法。迭代子将容器中的元素抽象为一个序列,为后面的算法提供了一个容器的通用界面,同时迭代子提供了一个数据访问的标准模型。也缓解了要求容器提供一组更广泛操作的压力。
    8.4.4算法
      容器解决的是存储问题也就是数据结构的问题,但是标准容器只定义了很少的基本操作,所以要标准库提供更多操作。标准库中的算法称为泛型算法,称为算法是因为它们实现共同的操作;称为泛型是因为它们可操作在多种容器类型上,既包括内置类型、标准库中的类型和用户自定义的与标准库兼容的容器类型。

猜你喜欢

转载自blog.csdn.net/qq_40719550/article/details/83444597
今日推荐