通用编程技术(generic programming)
面对对象编程关注的是编程的数据方面,而通用编程技术关注的是算法。它们之间的共同点是抽象和创建可重用代码。
通用编程技术旨在编写独立于数据类型的代码。在C++中,完成通用程序的工具是模板。
STL
STL提供了一组表示容器、迭代器、函数对象和算法的模板。
容器是一个与数组类似的单元,可以存储若干个值。STL容器是同质的,即存储的值的类型相同。
算法是完成特定任务(如对数组进行排序或在链表中查找特定值)的处方。
迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针。
函数对象是类似于函数的对象,可以是类对象或函数指针(包括函数名,因为函数名被用作指针).
STL使得能够构造各种容器(包括数组、队列和链表)和执行各种操作(包括搜索、排序和随机排列)。
各种STL容器模板都接受一个可选的模板参数,该参数指定使用哪个分配器对象来管理内存。
例如,vector模板的开头与下面类似:
template <class T,class Allocator = allocator<T> >
class vector { ...
所有的STL容器都提供了一些基本方法,其中包括:
size()——返回容器中元素数目、
swap()——交换两个容器的内容、
begin()——返回一个指向容器中第一个元素的迭代器、
end()——返回一个表示超过容器尾的迭代器。
迭代器
它是一个广义指针。它可以是指针,也可以是一个可对其执行类似指针的操作——如解除引用(如 operator*() )和递增(如operator++() )——的对象。
对指向迭代器的指针进行广义化使得STL能够为各种不同的容器类(包括那些简单指针无法处理的类)提供统一的接口。
每个容器类都定义了一个合适的迭代器,该迭代器的类型是一个名为iterator的typedef,其作用域为整个类。
例如,为vector的double类型规范声明一个迭代器:
vector<double>::iterator pd; // pd an iterator
假设scores是一个vector<double>对象:
vector<double> scores;
则可以使用迭代器pd执行这样的操作:
pd = scores.begin(); // have pd point to the first element
*pd = 22.3; // dereference pd and assign value to first element
++pd; // make pd point to the next element
超过结尾(past-the-end)。它是一种迭代器,指向容器最后一个元素后面的那个元素。因此,如果scores和pd的定义与前面的范例中相同,则可以用下面的代码来显示容器的内容:
for (pd = scores.begin(); pd != scores.end(); pd++)
cout << *pd << endl;
STL容器的其他方法
比如vector模板类还包含一些只有某些STL容器才有的方法。
push_back()方法将元素添加到容器末尾。这样做时,它将负责内存管理,增加容器的长度,使之能够容纳新的成员。
范例:
vector<double> scores; // create an empty vector
double temp;
while (cin >> temp && temp >= 0)
scores.push_back(temp);
cout << "You entered " << scores.size() << " scores./n";
每次循环都给scores对象增加一个元素。在编写或运行程序时,无须了解元素的数目。只要能够取得足够的内存,程序就可以根据需要增加scores的长度。
erase()方法删除容器中给定区间的元素。它接受两个迭代器参数,这些参数定义了要删除的区间。第一个迭代器指定区间的起始处,第二个迭代器位于区间终止处的后一个位置。
范例:
vector<double> scores(10);
scores.erase(scores.begin(), scores.begin()+2);
上述代码将删除第一个和第二个元素,即删除begin()和begin()+1指向的元素(由于vector提供了随机访问功能,因此vector类迭代器定义了诸如begin()+2等操作)。
区间[it1,it2]由迭代器it1和it2指定,其范围为it1到it2(不包括it2)。
insert()方法接受3个迭代器参数,第一个参数指定了新元素的插入位置,第二个和第三个迭代器参数指定了被插入区间,该区间通常是另一个容器对象的一部分。
范例:
vector<int> old;
vector<int> new;
...
old.insert(old.begin(),new.begin()+1,new.end());
上述代码将new对象中除第一个元素以外的所有元素插入到old对象的第一个元素的前面。
使用超尾元素,将新元素附加到尾部:
old.insert(old.end(),new.begin()+1,new.end());
STL非成员(non-member)函数
STL从更广泛的角度定义了适用于所有容器类的非成员函数。
for_each()函数
它接受3个参数,前两个是定义容器中区间的迭代器,最后一个是指向函数的指针(更普遍地说,最后一个参数是一个函数对象)。
该函数将被指向的函数应用于容器区间中的各个元素。被指向的函数不能修改容器元素的值。
random_shuffle()函数
它接受两个指定区间的迭代器参数,并随机排列该区间中的元素。
与可用于任何容器类的for_each不同,该函数要求容器类允许随机访问(vector类可以做到这一点)。
sort()函数
该函数也要求容器支持随机访问。
该函数有两个版本,第一个版本接受两个定义区间的迭代器参数,并使用为存储在容器中的类型元素定义的 < 操作符,对区间中的元素进行操作。
例如:
vector<int> coolstuff;
...
sort(coolstuff.begin(),coolstuff.end());
将按升序对coolstuff的内容进行排序,排序时使用内置的 < 操作符对值进行比较。
如果容器元素是用户定义的对象,则要使用sort(),必须定义能够处理该类型对象的operator<()函数。
第二个版本接受3个参数,前两个参数也是指定区间的迭代器,最后一个参数是指向要使用的函数的指针(函数对象)。修改了默认使用的operator<()函数,则可以使用用户自定义的方法进行排序。