C++ Primer chap12
STL的内容十分繁杂,可以专门作为一个专题来学。
由于现在还在入门阶段,所以不应过多的把精力投入到对标准库的研究上。
主要精力应该在C++的基本机制和特性上。做一些标准C++的简单练习。
基本掌握后才是进一步的研究库。
1 .
我们的min()函数是模板机制的功能强大性与局限性的一个很好例子
template <typename Type>
const Type&
min( const Type *p, int size )
{
int minIndex = 0;
for ( int ix = 1; ix < size; ++ix )
if ( p[ ix ] < p[ minIndex ] )
minIndex = ix;
return p[ minIndex ];
}
功能强大性来自于 定义一个min()的单个实例 它就可以被实例化为无限种类型 的能
力 局限性在于 虽然min()可以被实例化为无限种类型 但是它并不是对所有类型都完全适
用
局限性的焦点在于小于操作符的使用上 在一种情况下 底层类型可能不支持小于操作
符 例如 一个Image 类可能不提供小于操作符的实现 虽然我们现在还不知道 但是以后
可能希望发现Image 对象数组的最小帧数 但是用Image 类数组来实例化min()会导致编译时
刻错误
error: invalid types applied to the < operator: Image < Image
在第二种情况下 虽然存在小于操作符 但是提供的语义并不合适 例如 如果我们希
望找到最小的字符串 但是希望只考虑字母 并且不对大小写敏感 则虽然小于操作符被支
持 但支持的却是错误的语义
传统的方案是参数化比较操作符 在这种情况下声明一个函数指针 该函数有两个参数
并返回一个bool 型的值
template < typename Type,
bool (*Comp)(const Type&, const Type&)>
const Type&
min( const Type *p, int size, Comp comp )
{
int minIndex = 0;
for ( int ix = 1; ix < size; ++ix )
if ( Comp( p[ ix ], p[ minIndex ] ))
minIndex = ix;
return p[ minIndex ];
}
这种方案 与我们使用内置的小于操作符的第一个实现一起提供了对任何类型的一般性
支持 同时也包括我们的Image 类 只要我们实现两个Image 小于比较的语义 函数指针的
主要性能缺点是 它的间接引用使其不能被内联
对函数指针的替代策略是函数对象 我们在前面的例子中看到了大量的例子 函数对
象是一个类 它重载了函数调用操作符 operator() 该操作符封装了通常应该被实现为一
个函数的动作 典型情况下 函数对象被作为实参传递给泛型算法 当然我们也可以定义独
立的函数对象 例如 如果把类AddImage 定义为一个函数对象 它取两个图像 把它们合
成在一起 即把两个加在一起 然后返回一个图像 则我们可以这样定义
AddImages AI;
为了使函数对象执行其操作 我们应用调用操作符 提供必要的Image 类操作数 例如
Image im1("foreground.tiff"), im2("background.tiff");
// ...
// 调用 Image AddImages::operator()(const Image&,const Image&);
Image new_image = AI( im1, im2 );
函数对象与函数指针相比较 有两个方面的优点 首先 如果被重载的调用操作符是inline
函数 则编译器能够执行内联编译 提供可能的性能好处 其次 函数对象可以拥有任意数
目的额外数据 用这些数据可以缓冲结果 也可以缓冲有助于当前操作的数据
下面是修改后的min()实现 注意函数指针也可以用这个声明来传递 但是没有任何原型检查
template < typename Type,
typename Comp >
const Type&
min( const Type *p, int size, Comp comp )
{
int minIndex = 0;
for ( int ix = 1; ix < size; ++ix )
if ( Comp( p[ ix ], p[ minIndex ] ))
minIndex = ix;
return p[ minIndex ];
}
泛型算法一般支持两种形式来应用操作 使用内置 或可能是被重载的 操作符 和使
用函数指针或函数对象执行操作
函数对象从哪里来 一般来说 有三种来源
标准库预定义的一组算术 关系和逻辑函数对象
一组预定义的函数适配器 允许我们对预定义的函数对象 甚至于任何函数对象
进行特殊化或者扩展
2.
预定义函数对象被分成算术 关系和逻辑操作 每个对象都是一个类模板 其中操作数
的类型被参数化 为了使用它们 我们必须包含下列头文件
#include <functional>
例如 支持加法的函数对象是一个名为plus 的类模板 为定义一个可以把两个整数相加
的实例 我们可以这样写
#include <functional>
plus< int > intAdd;
plus< int > int Add;
为了调用加法操作 我们把重载的调用操作符应用在intAdd 上 就像在上节中我们对
AddImage 类所做的那样
int ival1 = 10, ival2 = 20;
// 等价于 int sum = ival1 + ival2;
int sum = intAdd( ival1, ival2 );
2
C++语言要求绑定在const vector 上的iterator 也必须是const iterator
我们这样做
// ok: 这次可以通过编译了
vector< type >::const_iterator iter = vec.begin();