C++ STL源码分析——适配器之迭代器适配器

【侯捷-SL体系结构内核分析-适配器】

reverse_iterator
inserter_iterator

reverse_iterator

容器除了拥有返回正向迭代器的函数 begin 和 end 之外,还有两个类似的成员函数 rbegin 和 rend,它们返回的是容器的逆向迭代器,它们之间的相互关系如下:
在这里插入图片描述

  • begin() 返回指向容器头元素的正向迭代器;end() 返回指向容器尾元素的下一个位置的正向迭代器。
  • rbegin() 返回指向容器尾元素的逆向迭代器;rend() 返回指向容器头元素的上一个位置的逆向迭代器。
实现原理

逆向迭代器就是正向迭代器的一个 adapter,它的实现原理也很简单,相关源代码如下:

	_NODISCARD reverse_iterator rbegin() noexcept
		{	// return iterator for beginning of reversed mutable sequence
		return (reverse_iterator(end()));
		}
		
	_NODISCARD reverse_iterator rend() noexcept
		{	// return iterator for end of reversed mutable sequence
		return (reverse_iterator(begin()));
		}
template<class _BidIt>
	class reverse_iterator
	{	// wrap iterator to run it backwards
public:
...
	_NODISCARD _CONSTEXPR17 reference operator*() const
		{	// return designated value
		_BidIt _Tmp = current;
		return (*--_Tmp);
		}
	_CONSTEXPR17 reverse_iterator& operator++()
		{	// preincrement
		--current;
		return (*this);
		}
protected:
	_BidIt current;	// the wrapped iterator
	};
  • 内含一个被适配的正向迭代器。可以看到,该正向迭代器必须为 bidirectional_iterator 类型。
  • 对逆向迭代器解引用,取得的是它对应的正向迭代器的上一个元素值,所以对当前临时正向迭代器自减后再解引用。
  • 逆向迭代器的自增操作相当于正向迭代器的自减操作。

inserter_iterator

先看下面一段代码:

void main()
{
	vector<int> vec1 = { 1, 2 ,3 ,4 ,5 };
	//先分配5个数据的内存大小
	vector<int> vec2(5);

	copy(vec1.begin(), vec1.end(), vec2.begin());
}

当我们利用 copy 函数将源容器的元素拷贝到目标容器时,我们必须先分配好第二个容器的内存足以放下即将被拷贝的数据
为什么需要先分配好内存呢?可以在回顾看看 copy 关键源代码:

template<class _InIt,
	class _OutIt> inline
	_OutIt _Copy_unchecked1(_InIt _First, _InIt _Last,
		_OutIt _Dest, _General_ptr_iterator_tag)
	{	// copy [_First, _Last) to [_Dest, ...), arbitrary iterators
	for (; _First != _Last; ++_Dest, (void)++_First)
		{
		*_Dest = *_First;
		}

	return (_Dest);
	}

可以看到,从源端到目标端采取的直接是一个赋值动作,将源端的值赋值给目标端,所里目标端当然需要事先分配好内存。

再来看看利用 inserter 适配器写出的拷贝操作:

void main()
{
	list<int> source, destination;

	for (int i = 1; i <= 5; ++i)
	{
		source.push_back(i);
		destination.push_back(i * 10);
	}

	list<int>::iterator it = destination.begin();
	advance(it, 3);

	copy(source.begin(), source.end(), inserter(destination, it));
}

将源端的元素拷贝到目标端时,并没有事先在目标端分配内存,而是直接使用 inserter 适配器,将源端元素拷贝插入到目标端中,无需调用者事先分配内存。现在来探探 inserter 内部实现原理。
inserter 的实现源代码如下:

template<class _Container>
	_NODISCARD inline insert_iterator<_Container> inserter(_Container& _Cont,
		typename _Container::iterator _Where)
	{	// return insert_iterator
	return (insert_iterator<_Container>(_Cont, _Where));
	}

内部返回了一个 inserter_iterator 对象。

template<class _Container>
	class insert_iterator
	{	// wrap inserts into container as output iterator
public:
...
	insert_iterator& operator=(const typename _Container::value_type& _Val)
		{	// insert into container and increment stored iterator
		iter = container->insert(iter, _Val);
		++iter;
		return (*this);
		}
protected:
	_Container * container;	// pointer to container
	typename _Container::iterator iter;	// iterator into container
	};

可以看到,inserter_iterator 它内涵目标容器成员,它对赋值操作进行的重载。所以在 copy 函数内部,当将源容器元素赋值给目标容器,也就是下面这句代码:

*_Dest = *_First;

会调用 *_Dest, 也就是 inserter_iterator 的重载 = 函数,在这个函数内部,又会调用 目标容器的 insert 函数,我们知道,容器的 insert 成员函数就是实现自动分配内存和赋值,由此就实现了 copy 函数的自动分配内存和拷贝赋值。

是不是很神奇!!amazing!!

有一个小细节不知道注意到没有。在重载函数内部,当在目标容器内插入元素后,返回的指向插入位置的 iterator 进行了自增动作。但是 copy 函数内部在赋值完后,又做了一次自增动作,这样岂不是往后移动了两个元素位置?
实际上,在 copy 函数内部,是 insert_iterator 这个对象进行了自增操作,而 insert_iterator 已经对自增操作符进行了重载,如下所示,它实际上并没有做任何动作,仅仅返回当前对象。所以,插入时迭代器的顺延动作是由 insert_iterator 类自己负责的。

	insert_iterator& operator++()
		{	// pretend to preincrement
		return (*this);
		}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值