1.适配器
-
三种类型的适配器:容器适配器,迭代器适配器,函数适配器
-
容器适配器:用来扩展7种基本容器,利用基本容器扩展形成了栈、队列和优先级队列
利用现有容器扩展而成,所以称之为容器适配器 -
迭代器适配器:(反向迭代器、插入迭代器、IO流迭代器)
反向迭代器利用正向迭代器来实现的;
插入迭代器在遍历的过程中可以插入数据;
IO流迭代器可以遍历IO流对象; -
函数适配器:函数适配器能够将仿函数和另一个仿函数(或某个值、或某个一般函数)结合起来。
2.函数适配器
-
函数适配器:函数适配器能够将仿函数和另一个仿函数(或某个值、或某个一般函数)结合起来。
-
eg:P83\01.cpp
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
bool is_odd(int n)
{
return n % 2 == 1;
}
int main(void)
{
int a[] = {1,2,3,4,5};
vector<int> v(a, a+5);
//算法可以适用于容器的
//统计向量中奇数的个数
cout<<count_if(v.begin(), v.end(), is_odd)<<endl;
return 0;
}
-
测试:
-
eg:P83\02.cpp
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
bool is_odd(int n)
{
return n % 2 == 1;
}
int main(void)
{
int a[] = {1,2,3,4,5};
vector<int> v(a, a+5);
//算法可以适用于容器的
//统计向量中奇数的个数
count<<count_if(v.begin(), v.end(), is_odd)<<endl;
//计算奇数元素的个数
// 这里的bind2nd将二元函数对象modulus转换为一元函数对象。
//modulus本来的参数接收2个参数:m%n,
//bind2nd只需要接受一个参数了,因为他绑定了第2个参数,m%2,此时一元函数对象有状态了,保存的是2的状态
//而m的参数从v.begin(), v.end()中来
//bind2nd(op, value) (param)相当于op(param, value)
//bind2nd(二元函数对象,是要绑定的值),返回一元函数对象,返回一个参数param
//param是v.begin(), v.end(),遍历过程中,将值传递到param里面的
//bind2nd是一个函数模板
cout << count_if(v.begin(), v.end(),
bind2nd(modulus< int>(), 2)) << endl;
return 0;
}
-
测试:
-
断点:
bind2nd(modulus< int>(), 2)) << endl;
返回值是一元函数对象
F11,看下构造函数,第一个参数是二元函数对象,第二个参数是要绑定的值
为什么说binder2nd是一元函数对象?
因为他继承至unary_function
重载()运算符,只接受一个参数说明他是一元函数对象
-
eg:P83\03.cpp
-
测试:
3.针对成员函数的函数适配器
- 将类的成员函数转换为函数对象
- eg:P83\04.cpp
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <string>
using namespace std;
class Person
{
public:
Person( const string &name) : name_(name) {}
void Print() const
{
cout << name_ << endl;
}
void PrintWithPrefix(string prefix) const
{
cout << prefix << name_ << endl;
}
private:
string name_;
};
void foo( const vector<Person> &v)
{
//for_each接收的是一元的函数对象
// mem_fun_ref将不带参数的成员函数转换成一元函数对象,将print()成员函数转换成立一元函数对象
// mem_fun_ref是一个函数适配器
for_each(v.begin(), v.end(), mem_fun_ref(&Person::Print));
//mem_fun_ref将带一个参数的成员函数转换成二元函数对象
//称之为:mem_fun_ref函数模板的类型自动推导功能,可以根据传递进来的参数进行类型推导
for_each(v.begin(), v.end(), bind2nd(mem_fun_ref(&Person::PrintWithPrefix), "person: "));
}
void foo2( const vector<Person *> &v)
{
//mem_fun_ref用于容器里面的元素是对象
//mem_fun用于容器里面的元素是指针
for_each(v.begin(), v.end(), mem_fun(&Person::Print));
for_each(v.begin(), v.end(), bind2nd(mem_fun(&Person::PrintWithPrefix), "person: "));
}
int main( void)
{
vector<Person> v;
v.push_back(Person( "tom"));
v.push_back(Person( "jerry"));
foo(v);
//向量中存放的是对象指针
vector<Person *> v2;
v2.push_back( new Person( "tom"));
v2.push_back( new Person( "jerry"));
foo2(v2);
return 0;
}
- 断点:
for_each(v.begin(), v.end(), mem_fun_ref(&Person::Print));
函数适配器mem_fun_ref,形式上来看是一个函数模板,(_Ty::\*_Pm)() const表示类里面的不带参数的成员函数,且是const的成员函数,
F11,_Pm表示将Person::Print的成员函数传递进来
F11,说明mem_fun_ref是一元函数对象。
调用构造函数发现,他将成员函数的地址保存到_Pmemfun。
重载了调用运算符(),_Left表示Person对象,然后调用他的成员函数
- 断点:
for_each(v.begin(), v.end(), bind2nd(mem_fun_ref(&Person::PrintWithPrefix), "person: "));
都是模板,构成重载
这是一个二元的函数对象,_Left传递进来的是一个类对象,_Right传递进来的是一个参数
- 区别在于是对象还是对象指针
- 断点:
for_each(v.begin(), v.end(), mem_fun(&Person::Print));
将不带参数的成员函数转换成一元函数对象
F11,通过指针来调用成员函数
-
测试:
-
对一般函数的函数适配器
eg:P83\05.cpp
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <string>
using namespace std;
int main( void)
{
char *a[] = { "", "BBB", "CCC"};
vector< char *> v(a, a + 2);
vector< char *>::iterator it;
//ptr_fun转换成二元的函数对象,a,b,最后是strcmp(value, ""),如果是空字符串,比较返回0,为假
//find_if是判别式为非0的时候,才是我们需要的
// bind2nd转换成一元函数对象
it = find_if(v.begin(), v.end(), bind2nd(ptr_fun(strcmp), ""));//查找第一个不是空串的字符串
if (it != v.end())
cout << *it << endl;
return 0;
}
-
测试:
-
eg:P83\06.cpp
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <string>
using namespace std;
bool check( int elem)
{
return elem < 3;
}
int main( void)
{
int a[] = { 1, 2, 3, 4, 5};
vector< int> v(a, a + 5);
vector< int>::iterator it;
// not1中的no说明是>=3,取反
// not1也是一个一元函数对象
it = find_if(v.begin(), v.end(), not1(ptr_fun(check)));//返回第一个>=3的元素
//不能这么写的原因是:因为类型不匹配, not1需要有argument_type,而check函数没有
//ptr_fun是一个函数适配器,将普通函数转换成一元函数对象,这样与not1的接口能匹配了
// it = find_if(v.begin(), v.end(), not1(check);
if (it != v.end())
cout << *it << endl;
return 0;
}
-
测试:
go进去
只有转换成unary_function才会有argument_type
4.std::find用法
find() 函数本质上是一个模板函数,用于在指定范围内查找和目标元素值相等的第一个元素。
如下为 find() 函数的语法格式:
InputIterator find (InputIterator first, InputIterator last, const T& val);
其中,first 和 last 为输入迭代器,[first, last) 用于指定该函数的查找范围;val 为要查找的目标元素。
正因为 first 和 last 的类型为输入迭代器,因此该函数适用于所有的序列式容器。
另外,该函数会返回一个输入迭代器,当 find() 函数查找成功时,其指向的是在 [first, last) 区域内查找到的第一个目标元素;如果查找失败,则该迭代器的指向和 last 相同。
值得一提的是,find() 函数的底层实现,其实就是用运算符将 val 和 [first, last) 区域内的元素逐个进行比对。这也就意味着,[first, last) 区域内的元素必须支持运算符。
eg:
- 可以看到,find() 函数除了可以作用于序列式容器,还可以作用于普通数组。
#include <iostream> // std::cout
#include <algorithm> // std::find
#include <vector> // std::vector
using namespace std;
int main() {
//find() 函数作用于普通数组
char stl[] ="http://c.biancheng.net/stl/";
//调用 find() 查找第一个字符 'c'
char * p = find(stl, stl + strlen(stl), 'c');
//判断是否查找成功
if (p != stl + strlen(stl)) {
cout << p << endl;
}
//find() 函数作用于容器
std::vector<int> myvector{ 10,20,30,40,50 };
std::vector<int>::iterator it;
it = find(myvector.begin(), myvector.end(), 30);
if (it != myvector.end())
cout << "查找成功:" << *it;
else
cout << "查找失败";
return 0;
}
参考: