1.何谓泛型算法
所谓泛型算法,提供了许多可施于容器类及数组上的操作行为,之所以将这些算法称为泛型,是因为这些操作与它们所要作用于的对像的数据类型(如,int,double等)无关,并且与
容器类型(如,vector,list,map等)也无关.
1.1如何实现与作用对像的数据型别(类型)无关
这正是应用了前面第二章中我们学到的模板函数来实现的.
例如,我们要实现一个查打算法find(),用以在vector内查找目标值,如果查到就返回指向其的指针或返回其地址,如果找不到就返回0.而vector内无素是什么类型并不确定,可以是
int型,也可以是double型,或者是string等.
对于这个问题,想要实现的话当然必须要求元素无论是什么类型,但这些类型应当都具有equality(==)运算.
下面就是实现:
template<typename elemType>
elemType *find(vector<elemType> &vec,elemType &e)
{
int i;
for(i=0;i<vec.size();i++)
if(vec[i]==e) return &vec[i]; //这的&号不同于形参中的&,形参中是表引用,传址,而这是表取址
return 0;
}
这个实现很好理解,既然类型不定,那么我们就通过template<typename elemType>这句话将类型假定为elemType,当然也可取其他名称,反正都只是起指代作用.
但是这些类型必须具有equality(==)运算.
1.2如何实现与容器类型不相关
这就要求我们不要直接在容器上操作,就是说我们编的函数形参不能是容器类型,例如上面第一个参数就是vector类型,在这里是行不通的.取而代之的是我们只需要
把所以操作对像的区间参进去,至于这段区间内元素之间关系是什么样的,我们并不用去理它,这些交给底层去做.
那么怎么传区间呢,第一种想法是传首址和长度.第二种想法是传两个指针分别指向首尾元素.
1.2.1传首址和长度
template<typename elemType>
elemType *find(elemType *array,int size,elemType value)
{
int i;
if(!array||size<1) return 0;
for(i=0;i<size;i++)
if(array[i]==value) return &array[i];
return 0;
}
1.2.2传首尾元素指针
上面第一种做法还是不彻底,还是有数组(array)的影子.假如我们设两个指针first,last分别指向首元素和最后个元素的下一位置,这样就不用理会是什么容器类型了.
template<typename elemType>
elemType *find(elemType *first,elemType *last,elemType &value)
{
if(!first||!last) return 0;
for(;first!=last;first++)
if(*first==value) return first;
return 0;
}
注意,last是指向最后个元素的下一个位置的,只起到哨兵角色,并不能对之进行读取操作,否则会越界.
2.泛型指针Iterators
注意到了没有,上面在实现操作与容器类型无关时,无论是array还是vector都是顺序存储的,我们在for循环迭代中都是用++的形式进行取下一个元素.
但如果容器换成list呢,它是链式存储的.难不成又得专门写一个对list的函数.
其实,我们可以在1.2.2的基础上进一步泛化抽像.1.2.2中的first指针和last指针,虽然实现了数据类型无关和一定程度上的容器无关性,注意只是一定程度,比如还不能
完全实现存储结构的无关性,上面1.2.2只适合于顺序存储.我们可以对这对指针进行抽像泛化,变成泛化指针Iterators class,即不考虑它的存储结构,到于怎么取下一个无素地址,我们交给iterators class内部中的内联函数去实现.对于list iterator 来说,其递增到下一个元素可以通过沿着前进指针实现,而vector iterator取下一个元素,可以将当前地址加上一个数据类型大小得到.
2.1定义一个iterator对象
vector<string>:: iterator iter;//这样就定义了一个iterator对象iter
vector <string>::表示iterator是vector内的嵌套定义
2.2获得一个iterator对象
每个容器都提供了begin()函数,这个函数返回值是iterator对象,用以指向容器的第一个元素,也同时提供了end()函数,返回的iterator对象是指向最后个元素的下一位置.
int ia[5]={1,2,2,3,5};
vector<int> ivec(ia,ia+5);
vector<int>:: iterator first,last;
first=ivec.begin();
last=ivec.end();
对find()函数进行改造
template<typename iteratorType, typename elemType>
iteratorType find(iteratorType first,iteratorType last,elemType value)
{
for(;first!=last;++first)
if(value==*first)
return first;
return last;
}
2.3所有容器及string类都有的操作
equality(==)和inequality(!=)运算符;
赋值运算符(=);将某个容器复制给另一个容器
empty();
size();
clear();清空所有元素
begin();
end();
insert();
erase();删除元素,抹除操作
3.序列式容器
主要有三种:
vector向量:顺序存储
list:双链式存储
deque(读作deck)即double-ended queue双端队列,是一种具有队列和栈性质的数据结构,与vector相同,都是顺序存储,但与vector相比,其插入删除操作限定在两端执行.
使用序列式容器,必须含入相关的头文件
#include<vector>
#include<list>
#include<deque>
3.1定义序列式容器对象
有五种方式
3.1.1定义产生空容器
vector<int> ivec;
3.1.2 产生特定大小容器,每个元素都被默认初始化了
vector<int > ivec(1024);
3.1.3指定大小,并且为每个元素指定初始值
vector <int > ivec(10,1);
3.1.4 指定区间,通常用一对iterators 来分别指定首址和最后个元素的下一个位置
int ia[5]={0,12,1,2,5};
vector<int> ivec(ia,ia+5);
3.1.5 复制容器
vector<int> ivec1(10,1);
vector<int> ivec2(ivec1);