1、需求描述v-1
需求:用户给予一个整数vector,我们必须返回一个新的vector,其中内含原vector之中小于10的所有数据。
根据这个需求可以很容易的设计一个如下的函数 v1_less_than_10():
using vecInt = vector<int>;
vecInt v1_less_than_10(const vecInt& vec)
{
vecInt nVec;
for (size_t ix = 0; ix < vec.size(); ix++)
{
if (vec[ix] < 10)
nVec.push_back(vec[ix]);
}
return nVec;
}
v1_less_than_10():函数是最简单直接的一种实现方式,但是存在一个一个问题:假如用户下次想返回一个小于11的所有数据组成的vector,那就要重新建立一个新的函数,比如:less_than_11,那显然不够通用化!
2、需求描述v-2
需求:可以让用户指定某个上限值。
根据这个需求,我们可以在函数中添加表示上限值的一个参数,函数设计如下所示:
vecInt v2_less_than(const vecInt& vec, int less_than_val)
{
vecInt nVec;
for (size_t ix = 0; ix < vec.size(); ix++)
{
if (vec[ix] < less_than_val)
nVec.push_back(vec[ix]);
}
return nVec;
}
函数增加一个参数 less_than_val,新的vector中的数值都小于less_than_val,稍微通用化了一些,但是这个函数只能用来处理 < 的情况,而不能处理 > 的情况。
3、需求描述v-3:函数作为参数
需求:可以让用户指定某个操作,比如 大于、小于等。
根据需求描述,有一个解法,以函数调用取代比较运算符。加入第三个参数pred,用它来指定一个函数指针,其参数列表有两个函数,返回值为bool。形如: bool (*pred)(int , int); 新的函数定义如下所示:
/*
vecInt v3_filter(const vecInt& vec,
int less_than_val,
bool(*pred)(int, int));
*/
//也可以采用下面的形式(推荐):
using Func = bool(*)(int, int); //c++11
vecInt v3_filter(const vecInt& vec, int less_than_val, Func func);
vecInt v3_filter(const vecInt& vec, int less_than_val, Func func)
{
vecInt nVec;
for (size_t ix = 0; ix < vec.size(); ix++)
{
if (func(vec[ix], less_than_val))
nVec.push_back(vec[ix]);
}
return nVec;
}
v3_filter(),新添加了一个参数,这个参数是一个函数指针,所以在调用它之前需要有相关的函数传参给它,定义如下两个函数:
//此时,需要先自定义Func类型的关系比较函数:有两个参数、必须返回bool
bool less_than(int v1, int v2) // < 操作符:v1<v2则返回true,否则返回false
{
return v1 < v2 ? true : false;
}
bool greater_than(int v1, int v2) // > 操作符:v1>v2则返回true,否则返回false
{
return v1 > v2 ? true : false;
}
v3_filter()函数调用方式如下所示:
//函数调用
void play01_2()
{
vecInt vec{ 6,10,8,4,10,7,10 };
int num = 10;
vecInt nVec = v3_filter(vec, num, less_than);
vecInt nVec2 = v3_filter(vec, num, greater_than);
}
4、需求描述v-4:Function Object和Binder Adapter
需求:使用标准库中的function object实现提取小于某个值的vector。
在v3_filter()中,我们使用的for循环来寻找满足条件的数据元素,其实也可以用STL中的算法find_if()来进行查找
find_if():
原型:template <class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred);
功能:Find element in range
返回:Returns an iterator to the first element in the range[first, last) for which pred returns true.
If no such element is found, the function returns last.
需要注意的是,pred是一个一元函数(unary function,只能接收一个参数)。而上面v-3版本的 Func 函数类型是自己定义的,有两个参数,不满足一元函数的要求。那能不能用STL自带的函数呢?
当然是可以的。但是在STL中不称为函数,而是函数对象(function object)。
4.1 Function Object
所谓函数对象:是某种class的实例对象, 这类class对象对function call运算符做了重载操作,如此以来可是function object被当成一般函数来用。
function object实现了我们原本可能以独立函数加以定义的事务,那又何必呢?主要是为了效率,我们完全可以另call运算符成为inline函数,从未消除“通过函数指针来调用函数”时需付出的额外的代价。
STL事先定义了一组function object,分为算术运算符(Arithmetic)、关系运算符(Comparison)、逻辑运算符(Logical),如果要使用标准库中的function object,需要包含头文件<functional>。(C++98)
标准库中的大小比较,比较常见的有less<type>、greater<type>,下面以less为例:
less:函数对象要求必须有两个参数,并且会返回一个bool值。
原型定义:
template <class T> struct less : binary_function <T, T, bool>
{
bool operator() (const T& x, const T& y) const
{ return x<y; }
};
用法:Objects of this class can be used on standard algorithms such as sort, merge or lower_bound.
比如:sort(first_iterator, last_iterator, less<int>());
less<int>()会产生一个未命名的class template object,传给sort,然后sort会利用这个对象进行数值比较。
经过分析,发现上述的less并不那么符合我们的需求,因为,less期望外界传入两个值,如果第一个小于第二个就返回true,而本例中每个元素都必须和用户所指定的数值进行比较,而正常情况下,我们并不能指定less的某一个参数。
针对上述情况,可以将less<type>转化为一个一元(unary)运算符。这可以通过“将其第二个参数绑定(bind)至用户指定的数值”完成。这样一来,less<type>便会将每个元素拿出来一一与用户指定的数值进行比较。
可以通过标准库提供的所谓adapter(适配器)实现上述功能。
4.2 Binder Adapter
adapter(适配器):所谓binder adapter(绑定适配器)会将function object的参数绑定至某特定值,使binary(二元)function object 转换为unary(一元)function object。
STL提供了两个binder adapter:bind1dt会将指定值绑定至第一操作数,bind2nd则将指定值绑定至第二操作数。
函数实现:
//函数实现
vecInt v4_filter(const vecInt& vec, int val, less<int>& lt)
{
vecInt nVec;
vecInt::const_iterator iter = vec.begin();
while ((iter = find_if(iter,
vec.end(),
bind2nd(lt, val))) != vec.end())
{
nVec.push_back(*iter);
iter++; //遍历寻找下一个
}
return nVec;
}
可以看到在find_if()的第三个参数调用了bind2nd()函数。
函数调用:
//函数调用
void play01_3()
{
vecInt vec{ 6,10,8,4,10,7,10 };
int num = 10;
vecInt nVec = v4_filter(vec, num, less<int>());
}
5、需求描述v-5: Function Template
需求:消除filter()与vector元素类型,以及filter()与vector容器类型的依赖关系,使filter()更加泛型化;
为了消除它和元素类型间的依赖性,我们将filter()改为function template,并将元素类型加入到template的声明中;
为了消除它和容器类型间的依赖性,可以传入一对iteartor[first,last),并在参数列表中增加另一个iterator,用以指定从何处开始复制元素。
函数实现:
//函数实现
template<typename InputIterator, typename OutputIterator, typename ElemType, typename Comp>
OutputIterator v5_filter(InputIterator first, InputIterator last,
OutputIterator at, const ElemType &val, Comp pred)
{
while ((first = find_if(first,
last,
bind2nd(pred, val))) != last)
{
cout << "found value: " << *first << endl;
*at++ = *first++;
}
return at;
}
函数调用:
//函数调用
void play01_4()
{
int num = 10;
const int elem_size = 8;
int ia[elem_size] = { 6,10,8,4,10,7,10 };
vecInt vec1(ia, ia+elem_size);
//下面这个容器用来存储过滤效果
int ia2[elem_size];
vecInt vec2(elem_size); //初始化了大小
cout << "filtering int array for value less than 8:\n";
v5_filter(ia, ia+elem_size, ia2, num, less<int>());
cout << "\nfiltering int vector for value less than 8:\n";
v5_filter(vec1.begin(), vec1.end(), vec2.begin(), num, less<int>());
shoeVec(vec2);
}
运行结果如下所示:
可以看到最终打印新的vector时,里面有几个元素为0的数据,这是因为在定义新的vectror:vec2时,指定了初始化时的大小,有可能填不满vec2,这样就会造成后面几个元素没有数据。
6、需求描述v-6: Function Template和Insertion Adapter
需求:返回任意大小的vector,用以存储小于某个特定值的数据(个数不定、长度不定);
在上面的play01_4()演示中,定义了一个新的vector:vec2用以存储返回的数据,在初始化时指明了该容器的容量。
而在v5_filter()的实现中,将源端(容器)中每一个符合条件的元素一一赋值(assign)至目的端(容器),这种形式下,目的端的容器必须足够大,以储存被赋值进来的每个元素;
但是,v5_filter()没办法知道每次对at递增至后,at是否仍指向一个有效的容器位置。“确保at所指目的端容器空间够大”这是程序员的责任。那意味着,对于那些采用赋值(assigned)操作的算法,我们必须总是传入某个固定大小的容器至上述算法吗?这样也不符合STL的精神!
标准库提供了三个所谓的insertion adapter,这些 adapter让我们得以避免使用容器的assgnment运算符:
6.1 Insertion Adapter
back_inserter: 会以容器的push_back()函数取代assignment运算符。对于vector来说,这是比较适合的inserter.传入back_inserter的参数,应该就是容器本身:
vector<int> result_vec;
unique_copy(ivec.begin(), ivec.end(), back_inserter(result_vec));
inserter: 会以容器的inserter函数取代assignment运算符。inserter()接受两个参数:一个是容器,另一个是iterator,指向容器内的插入操作起点:
vector<string> svec_res;
unique_copy(ivec.begin(), ivec.end(), inserter(svec_res, svec_res.end()));
front_inserter: 会以容器的push_front()函数取代assignment运算符,这个inserter只适用于list和deque:
list<int> ilist_clone;
unique_copy(ilist.begin(), ilist.end(), front_inserter(ilist_clone));
要使用上述适配器,必须包含头文件<iterator>。但是这些适配器不能用在array上,因为array不支持插入操作。
函数实现部分仍然是v5_filter(),只是调用方式有些不同,函数调用:
//函数调用
void play01_5()
{
int num = 10;
const int elem_size = 8;
int ia[elem_size] = { 6,10,8,4,10,7,10,9 };
vecInt vec1(ia, ia + elem_size);
//下面这个容器用来存储过滤效果
int ia2[elem_size];
vecInt vec2; //未初始化大小
cout << "filtering int array for value less than 8:\n";
v5_filter(ia, ia + elem_size, ia2, num, less<int>());
cout << "\nfiltering int vector for value less than 8:\n";
v5_filter(vec1.begin(), vec1.end(), back_inserter(vec2), num, less<int>());
shoeVec(vec2);
}
上述函数调用部分,使用了back_inserter()适配器。
运行结果: