Chapter 3 泛型编程风格(Generic Programming)
Standard Template Library(STL) 主要由两部分组成:一是 容器(container),包括vector、list、set、map等类;另一部分是用以操作这些容器的 泛型算法(generic algorithm),包括find()、sort()、replace()、merge()等。
顺序性容器(sequential container): vector、list deque
关联性容器(associative container): map set
泛型算法 通过 function template技术,达到“与操作对象的类型相互独立”。实现的关键在于,不直接在容器上操作,而是借由一对Iterator(first,last)标示要迭代的元素范围。
3.1 指针的算术运算
任务:给定一个储存int的vector,和一个整数值。如果vector中存在该值则返回一个指针指向vector中的该值的位置;否则返回0,标示该值不在vector中。
int* find(const vector<int> &vec, int value)
{
for ( int i = 0; i < vec.size(); i++ ){
if ( value == vec[i] )
return &vec[i]
}
return 0;
}
测试这个函数确实可以满足需求,如果要让其不仅可以处理int,更可以处理任何类型(该类型定义有equality运算符)。
只要我们将find()函数以function template呈现即可。
template <typename elemType>
elemType* find(const vector<elemType> &vec, elemType value)
{
for ( int i = 0; i < vec.size(); i++ ){
if ( value == vec[i] )
return &vec[i]
}
return 0;
}
新需求:让这个函数可以同时处理vector和array内的任意类型元素。
一个方法使用函数重载(overload)一份处理vector一份处理array。这不是本章节的重点所以不考虑这个方法。
面对难题我们可以 将该问题分解成数个较小、相对简单的问题。
我们可以将这个大问题分解成:(1)将array的元素传入,但是不指明array类型。(2)将vector的元素传入,但是不指明vector类型。
问题一解决方案:将array指针传入并且传入一个参数告知在何处停止读取;
解法一:增加一个参数表示数组的大小
template < typename elemType>
const elemType* find(const elemType *array, int size,
const elemType &value)
{
if ( !array || size < 1)
return 0;
for ( int i = 0; i < size; i++)
if ( array[i] == value )
return &array[i];
return 0;
}
解法二:传入另一个地址,指示array读取操作的终点。
template < typename elemType>
//first 表示数组第一个元素位置,last表示最后一个元素的下一地址;
const elemType* find(const elemType *first, const elelmType *last,
const elemType &value);
{
if ( !first || first == last)
return 0;
for ( ; first != last; first++)
if ( *first == value )
return first;
return 0;
}
传入last是合法的,但是对其进行读取或者写入都是不合法的。
问题二解法:
分析:问题二是说不论vector的元素类型是什么,都能依次访问其中的元素。array和vector都是以一块连续内存储存元素,所以我们可以使用和array一样的处理方式,将一组用来表示“起始地址/结束地址”的参数传入find()。但是vector可以是空的,array则不然。我们需要做的是获取vector的起始地址和结束地址然后传入上面的程序即可。
// 获取vector的起始地址
template <typename elemType>
inline const elemType* vbegin(const vector<elemType> &vec)
{
return vec.empty() ? 0 : &vec[0];
}
// 获取vector的结束地址
template <typename elemType>
inline const elemType* vend( const vector<elemType> &vec)
{
return vec.empty() ? 0 : &vec[vec.size()-1];
}
这样实现了find() ,只需这一份就同时处理vector和array。
如果还想扩展find()的功能,令其也能支持list类别。解决方案是:在底层指针的行为之上提供一层抽象,取代程序原本的“指针直接操作”。将底层指针的处理全都放在这个抽象层中,让用户不用直接面对指针操作。
3.2 了解Iterator(泛型指针)
Iterator(泛型指针)即上节中提到的取代“指针操作”的抽象层。通过C++的类机制实现。并提供了dereference(*)、inequality(!=)、increment(++)运算符。