配接器总结

配接器

实际上是一种设计模式:将一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的classes,可以一起运作。

container adapters

用于修改容器接口的配接器称为container adapter。

stl提供的两个容器queue和stack,其实都只不过是一种配接器。

iterator adapters

应用于迭代器身上的配接器叫做iterator adapters。包括insert iteratorsreverse iteratorsiostream iterators

insert iterators

1.迭代器的类型是output_iterator_tag,是输出迭代器。

2.每一个insert iterators内部都维护有一个容器,以back_insert_iterator为例:

template <class Container>
class back_insert_iterator {
protected:
	Container* container;
	...
}

这个容器可以由构造函数指定:

explicit back_insert_iterator(Container& x) : container(&x) {}

函数back_inserter实际上也是调用了这个构造函数,返回一个back_insert_iterator对象。

3.重载了=符号,对插入迭代器进行赋值操作,实际上会调用Container的插入操作(push_backpush_frontinsert)。比如对于back_insert_iterator

operator= (const typename Container::value_type& value) {
	container->push_back(value);
	return *this;
}

4.*++++(int)等符号也都重载,但是都是直接返回*this

5.insert_iterator常常用于copy函数,比如这样使用:

copy(ia + 1, ia + 3, front_inserter(vec));

实际上会不断对vec容器使用push_front操作,将前面输入迭代器的内容插入。

3和4中对=符号和*符号的重载共同实现了这种功能。因为copy的源码中有:

*result = *first;

当这里的result是插入迭代器时,由4可知*result得到的实际上还是result,于是变成了:

result = *first;

于是就会调用insert iterator的operator=()函数,进行插入操作。

5.对于insert_iterator类型,它有两个数据成员,除了container外,还保存一个指向容器的普通迭代器iter。因为在实现普通的插入操作时,还需要提供一个插入位置,这个iter就起这样的作用。
它的构造函数以及inserter函数都接收两个参数。

reverse iterators

1.reverse iterators的内部有一个数据成员current,是普通迭代器。它的构造函数就将普通迭代器作为参数。base()函数可以返回这个current。

2.reverse iterators的5种型别和current所属于的类别一样。

3.stl容器中,有正向迭代器的一般也有逆向迭代器(除了forward_list,没有逆向迭代器)。

typedef reverse_iterator<iterator> reverse_iterator;
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
...

可以看到,reverse iterators的尾后迭代器rend()存的是begin(),而rbegin()存的是end()

3.重载*函数:

reference operator*() const {
	Iterator tmp = current;
	return *--tmp;
}

也就是说,对reverse iterator解引用的时候,获得的是current的前一个迭代器(减1得到的)的值。

这也正好和容器的rbegin()以及rend()的规定匹配上了,rbegin()解引用取的是end()前一个迭代器的值,永远不会解引用end();而由于永远不会对rend()解引用,所以也永远不会对begin()进行--操作。不会出现问题。

4.reverse iterators的前进(++)操作,就是对current执行后退操作,reverse iterators的后退(--)操作,就是对current执行前进操作。

类似于前进n步和后退n步的操作,都是这样逆向的操作的。

stream iterator

1.将迭代器绑定在一个stream对象上,绑定在istream上的叫做istream_iterator,绑定到ostream上的叫做ostream_iterator。

2.istream_iterator迭代器类型是Input Iterator,ostream_iterator迭代器类型是Output Iterator

3.istream_iterator的实现:

有三个数据成员。一个是指向istream对象的指针,一个是value,还有一个bool变量指明是否读到了eof。

有一个成员函数read(),这个函数实际上就是通过<<符号从stream对象中将一个值输入给value。

有两个构造函数。默认构造函数绑定cin对象,并且将bool变量赋值为false。另一个构造函数接收一个istream对象,并且要执行一次read(),于是value会被赋值(或者会被阻塞)。

解引用操作,就是直接返回value。

++操作,就是调用一次read(),让输入流再输入一个值给value。

ostream_iterator的实现和istream_iterator类似。

4.ostream_iterator的实现:

有两个数据成员,一个是指向ostream对象的指针,一个是一个const char*类型的变量,名字叫做string。这个string的作用是作为间隔符。

有两个构造函数,一个只接受ostream对象,另一个还接受一个const char*类型变量。比如:

ostream_iterator<int> outiter(cout, ' ');

这个迭代器将数据输出到cout,每次间隔一个空格。

重载赋值操作=,调用它时,实际上是将value输出到ostream对象,并且如果间隔符号不为空,还要输出一个间隔符号。

*++等操作都什么也不做,直接返回*this

5.copy操作的实现。

比如outite是一个ostream_iterator,它会被当做copy函数的第三个迭代器参数,也就是result参数。

在拷贝的过程中,会不断做这样的事情:

*result = *first;

对result解引用会直接返回result,于是实际上是result = *first。调用赋值函数operator=,于是就将value输出到ostream对象中。

在迭代的过程中,会不断对result进行++操作,而它也直接返回result。

function adapters

1.container adapters在内部存一个容器,iterator adapters在内部存一个普通迭代器,同理,function adapters也在内部存仿函数的实例化对象。

2.以用于函数合成的compose1为例。

template <class Operation1, class Operation2>
class unary_compose
	: public unary_function<typename Operation2::argument_type,
							typename Operation1::result_type> {
...
}

将两个模板参数Operation1和Operation2,分别代表两个一元仿函数类型。
类继承一元仿函数unary_function。它的输入类型是Operation2的输入类型,输出类型是Operation1的输出类型。

protected:
	Operation1 op1;
	Operation2 op2;
public:
	unary_compose(const Operation1& x, const Operation2& y)
		: op1(x), op2(y) {}

两个数据成员分别是Operation1类型的对象和Operation2类型的对象。构造函数也是接受两个对象来分别初始化op1和op2。

typename Operation1::result_type
operator() (const typename Operation2::argument_type& x) const {
	return op1(op2(x));
}

仿函数经典操作,重载()符号,能看到参数类型是Operation2的参数类型,返回类型是Operation1的参数类型。而重载函数的实际过程也是,先计算op2(x),再将它的结果用op1计算,返回。

3.几种标准库内置的仿函数配接器:

  • not1not2:对返回值进行逻辑否定。一个处理一元仿函数,一个处理二元仿函数。

  • bind1stbind2nd:把二元仿函数处理成一元仿函数。它的构造函数会接受一个value参数,value绑定到二元仿函数的其中一个参数上,于是这个二元仿函数之后就只需要接受一个参数,表现得就像是一个一元仿函数。

  • compose1compose2:一个仿函数的结果作为另一个仿函数的输入,第二个仿函数的输出作为最终的输出。

  • ptr_fun:实现将函数指针当做仿函数来使用。将函数指针作为数据成员,重载的()实际上就是直接调用函数指针。
    注意函数指针的定义方式是: 函数返回值类型 (* 指针变量名) (函数参数列表);,比如书上所写的Result (*ptr) (Arg)

  • mem_funnum_fun_ref:这两个配接器实现将成员函数当做仿函数来使用。它们都存一个类的成员函数的指针,构造函数都接受一个指针。两者的区别在于,前者重载的()函数接受指针类型作为参数,而后者接受引用类型。
    S (T::*pf) (A),T是这个成员函数属于的类,S是这个成员函数的返回值类型,A是这个成员函数的参数类型(或者为空,表示不接受参数),pf就是这个指针。
    通过指针调用成员函数时,方法是return (p->*f)(x);先对函数指针f解引用,然后通过指针p调用这个函数。
    通过引用调用成员函数时,方法是return (p.*f(x));,先对函数指针f解引用,然后通过引用类型p调用这个函数。
    通过使用这两个配接器,可以在泛型算法中使用类的成员函数作为方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值