目录
●模板的概念
●在C++标准库中,几乎所有的代码都是模板代码。
●模板是对具有相同特性的函数或类的再抽象,模板是一种参数化的多态性工具。
●所谓参数化多态性,是指将程序所处理的对象的类型参数化,使一段程序代码可以用于处理多种不同类型的对象。
●采用模板编程,可以为各种逻辑功能相同而数据类型不同的程序提供一种代码共享的机制。
●模板通过参数实例化可以构建具体的函数或类,称为模板函数和模板类。
●函数模板
●通常情况下,程序中的算法就理论本身而言可以不受数据类型的限制,但通过代码用实现的各种算法和函数,却受数据类型的影响。
●如工程中经常应用的求解最大值和求绝对值等函数,当数据类型不同时,程序代码将有所不同。
数据为int或double时的求最大值问题
int Max(int x,int y)
{
if(x>y) return x;
else return y;
}
double Max(double x,double y)
{
if(x>y) return x;
else return y;
}
数据为int或double时的求绝对值问题
int abs(int a)
{
return a<0?-a:a;
}
double abs(double a)
{
return a<0?-a:a;
}
1. 函数模板的定义
模板函数模板是参数化的函数。
定义格式如下:
●template是模板定义的关键字。
● <模板形参表>中包含一个或多个用逗号分开的模板形式参数,每一项均由关键字class或typename引导一个由用户命名的标识符,此标识符为模板参数
●模板参数表示一种数据类型,可以是基本数据类型或类类型。该数据类型在发生实际函数调用时将被实例化,即用调用处的实际数据类型替代它。
●<模板形参表>中的每个模板参数都必须在参数表中得到使用,即作为形参的类型。
●参数表至少有一个参数说明,并且在函数体中至少使用一次。
●模板参数和基本数据类型一样,可以在函数中的任何地方使用。
2. 函数模板的使用
●函数模板只是一种说明,并不是一个具体的函数,C++编译系统不会产生任何可执行代码。
●当遇到具体的函数调用时,才根据调用处的具体参数类型,在参数实例化以后才生成相应的代码,此时的代码称为模板函数。
●普通函数只能传递变量参数,而函数模板提供了传递类型的机制。
#include <iostream.h>
template <class T> //模板定义,T为模板参数
T Max(T x,T y) //定义函数模板
{
return (x>y)?x:y;
}
int main()
{
int x1,y1;
float x2,y2;
double x3,y3;
cout<<"请输入2个整型数据,用空格分隔:"<<endl;
cin>>x1>>y1;
cout<<"The max of x1,y1 is:"<<Max(x1,y1)<<endl; //T为int
cout<<"请输入2个实型数据,用空格分隔:"<<endl;
cin>>x2>>y2;
cout<<"The max of x2,y2 is:"<<Max(x2,y2)<<endl; //T为float
cout<<"请输入2个双精度数据,用空格分隔:"<<endl;
cin>>x3>>y3;
cout<<"The max of x3,y3 is:"<<Max(x3,y3)<<endl; //T为double
}
3.模板函数的生成
●模板函数是函数模板的一个具体实例,是模板参数实例化后的一个可执行的具体函数,只处理一种确定的数据类型 。
●函数模板的实例化是在编译按系统处理函数调用时由系统自动完成。
●在调用函数模板时,系统首先确定模板参数所对应的具体类型,并依据该类型生成一个具本函数,系统实际上是调用了这个具有确定参数类型的函数。 (这种类型即可以是C++预定义的数据类型,也可以是用户自定义的数据类型)
●类模板与模板类
1.类模板的定义
●模板类的成员函数必须是函数模板。
●类模板中的成员函数的定义,若放在类模板的定义之中,则与类的成员函数的定义方法相同;
若在类模板之外定义,则成员函数的定义格式如下:
2. 类模板的使用
●类模板是参数化的类,必须先实例化为相应的模板类,并定义该模板类的对象以后才可以使用
●当类模板在程序中被引用时,系统根据引用处的参数匹配情况将类模板中的模板参数置换为确定的参数类型,生成一个具体的类。
类模板实例化
定义模板类的对象的格式
#include <iostream>
using namespace std;
template <typename T> //typename或class
class Square //类模板定义
{
T x;
public:
Square(T xx):x(xx){}
T fun(){
return x*x;}
};
int main()
{
Square <int> inta(15); //T置换为int生成模板类,并创建对象inta
Square <float> floata(16.5); //T置换为float的模板类,并创建对象floata
Square <double> doublea(15.55);//T置换为double的模板类,并创建对象doublea
cout<<"square of int data:"<<inta.fun()<<endl;
cout<<"square of float data:"<<floata.fun()<<endl;
cout<<"square of double data:"<<doublea.fun()<<endl;
}
●类模板的友元
类模板的友元和类的友元的特点基本相同,但也具有自身的特殊情况。
以类模板的友元函数为例,可以分为以下三种情况:
●友元函数无模板参数。
●友元函数含有类模板相同的模板参数。
●友元函数含有与类模板不同的模板参数。
#include<iostream>
#include<iomanip>
#include<cstdlib>
using namespace std;
const int Len=16;
template<class T> //模板定义
class SeqLn //顺序表类模板
{
public:
SeqLn<T>():size(0){} //类模板的构造函数
~SeqLn<T>(){} //类模板的析构函数
void Insert(const T &m,const int nst);//数据类型为模板形参T
T Delete(const int nst); //返回值类型为模板形参T
int LnSize()const {return size;}
private:
//转换函数模板的声明
friend void IntToDouble(SeqLn<int>&n,SeqLn<double>&da);
friend void Display(SeqLn<T>&mySeqLn);//类模板的友元
friend void Success(); //类模板的友元
private:
T arr[Len]; //数据元素为模板形参T
int size;
};
template<class T> //成员函数的模板定义
void SeqLn<T>::Insert(const T&m,const int nst)
{ if (nst<0||nst>size)
{ cerr<<"nst Error!"<<endl;
exit(1); }
if (size==Len)
{ cerr<<"the List is over,can't insert any data!"<<endl;
exit(1); }
for (int i=size;i>nst;i--)arr[i]=arr[i-1];
arr[nst]=m;
size++;
}
template<class T> //成员函数的模板定义
T SeqLn<T>::Delete(const int nst)
{ if (nst<0||nst>size-1)
{ cerr<<"nst Error!"<<endl;
exit(1); }
if (size==0)
{ cerr<<"the List is null,no data can be deleted!"<<endl;
exit(1); }
T temp=arr[nst];
for(int i=nst;i<size-1;i++)
arr[i]=arr[i+1];
size--;
return temp;
}
void Success()
{ cout<<"the Transform is over."<<endl;}
template<class T>
void Display(SeqLn<T> &mySeqLn)
{ for(int i=0;i<mySeqLn.size;i++)
{cout<<setw(5)<<mySeqLn.arr[i];
if((i+1)%8==0) //每行输出8个元素
cout<<endl;
}
}
template<class T1,class T2> //函数模板的模板定义
void IntToDouble(SeqLn<T1>&n,SeqLn<T2>&da)
{ da.size=n.size;
for(int i=0;i<n.size;i++)
da.arr[i]=double(n.arr[i]);
}
int main()
{ SeqLn<int>i_List; //定义int型顺序表类对象
SeqLn<double>d_List; //定义double型顺序表类对象
for(int i=0;i<Len;i++)
i_List.Insert(i,0);
IntToDouble(i_List,d_List);
Success();
Display(d_List); //输出转换后的结果
d_List.Delete(2); //执行删除操作
Display(d_List); //输出删除以后的结果
}
●STL标准库的相关内容(迭代器、算法、容器等)
1.STL简介
● STL(Standard Template Library)标准模板库,是由泛型算法和数据结构组成的通用库。
●包含在C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最具革新和挑战性的一部分。
●该库包含了许多在计算机科学领域里常用的基本数据结构和基本算法。
● STL类似于MFC和VCL,为广大软件设计人员提供了一个可扩展的应用框架,体现了软件的高度可复用性。
●与OOP的多态一样,STL是一种软件重用技术。
●STL体现了泛型程序设计思想,引入了许多新的理念:
需求(requirements) 概念(concept) 模型(model)
容器(container) 算法(algorithmn) 迭代器(iterator) …
2.STL主要组成:
●迭代器(iterator) ●算法(algorithms) ●容器(container)
●函数对象(function objects)
●适配器(allocators adapter) 容器和算法通过迭代器可以进行无缝连接。(string也可以认为是STL的一部分)
3. 容器(containers)
容器是STL的一个重要组成部分,涵盖了许多数据结构,如:
4. 算法(algorithms)
算法是STL的一个重要组成部分,包含了约100个通用算法,用于操控各种容器,同时也可以操控内建数组。如:
● find用于在容器中查找等于某个特定值的元素。
● for_each用于将某个函数应用到容器中的各个元素上。
● sort用于对容器中的元素排序。
5.迭代器(iterators)
●每个容器都有自己的迭代器,它类似于指针,算法通过迭代器来定位和操控容器中的元素。
6.函数对象
● STL的另一重要组成部分是函数对象。
● STL中包含了许多不同的函数对象,函数对象将函数封装在一个对象中,使得它可以作为参数传递给有关的STL算法。
●如同迭代器是指针的泛化一样,函数对象是函数的泛化。
●标准库提供两个类模板作为对象的基类:
std::unary_function std::binary_function 都在头文件<functional>中声明。
#include <iostream>
#include <vector> //向量容器类包含在vector中
#include <algorithm> //倒序算法
#include <utility> //迭代器
using namespace std;
void main()
{
vector <int> v(6); //定义一个有六个元素的向量类
v[0]=10;
v[1]=v[0]+10;
v[2]=v[0]+v[1];
v[3]=2*v[2];
v[4]=70;
v[5]=80;
cout<<"output element (original):"<<endl;
vector<int>::iterator pt; //定义一个迭代器pt,指向整型向量
for(pt=v.begin();pt!=v.end();++pt)
cout<<*pt<<" ";
cout<<endl;
reverse(v.begin(),v.end()); //调用倒序算法
cout<<"Output element (reverse):"<<endl;
for (int i=0;i<6;i++)
cout<<v[i]<<" ";
cout<<endl;
}