您的位置:首页 > Web前端

确保目标区间足够大(Effective stl 条款30)

2007-08-22 08:53 447 查看
[align=left] [/align]
[align=left]STL容器在被添加时(通过insert、push_front、push_back等)自动扩展它们自己来容纳新对象。这工作的很好,有些程序员因为这个信仰而被麻痹,认为他们不必担心要为容器中的对象腾出空间,因为容器自己可以照顾好这些。[/align]
[align=left] [/align]

vector<int> val;
//...; 给val容器设置初值
vector<int> res;
transform(val.begin(), val.end(),res.end(),negate<int>());//将val中的值取反后放入res容器

[align=left] [/align]
[align=left]上面的代码有bug,transform通过对目标区间的元素赋值的方法写入结果,transform会把negate<int>()应用于values[0],并把结果赋给*results.end()。然后它会把negate<int>()应用于value[1]并把结果赋给*(results.end()+1)。那只能带来灾难,因为在*results.end()没有对象,*(results.end()+1)也没有!调用transform是错误的,因为它会给不存在的对象赋值。[/align]
[align=left] [/align]
[align=left]看看transform的这部分代码就明白了:[/align]

// TEMPLATE FUNCTION transform WITH UNARY OP
template<class _InIt, class _OutIt, class _Fn1, class _InOutItCat>
inline
_OutIt _Transform(_InIt _First, _InIt _Last, _OutIt _Dest, _Fn1 _Func,
_InOutItCat, _Range_checked_iterator_tag)
{ // transform [_First, _Last) with _Func
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Dest);
_DEBUG_POINTER(_Func);

for (; _First != _Last; ++_First, ++_Dest)
*_Dest = _Func(*_First);//调用赋值操作符将结果赋给_Dest指向的位置,
//但并不检查这个位置是否有效
return (_Dest);
}

[align=left] [/align]
[align=left] [/align]

[align=left]确保算法有足够的元素存储输出数据的一种方法是使用插入迭代器(insert iterator)。插入迭代器是可以给基础容器添加元素的迭代器。通常,用迭代器给容器元素赋值时,被赋值的是迭代器所指向的元素。而使用插入迭代器赋值时,则会在容器中添加一个新元素。back_inserter和front_inserter函数都是迭代器适配器。迭代器适配器使用一个对象作为实参,并生成一个绑定在该容器上的插入迭代器。[/align]

transform(val.begin(),val.end(),back_inserter(results),
negate<int>());//OK

[align=left] [/align]

[align=left]transform函数写入的每一个值,都会通过back_inserter生成的插入迭代器实现。效果相当于在results上调用push_back。[/align]
[align=left] [/align]
为了更好的理解插入迭代器到底是什么东西,还是来看看back_inserter的源码吧:

[align=left]// TEMPLATE FUNCTION back_inserter[/align]
[align=left]template<class _Container> inline[/align]
[align=left]back_insert_iterator<_Container> back_inserter(_Container& _Cont)[/align]
[align=left] //back_inserter是迭代器适配器[/align]
[align=left]{ // return a back_insert_iterator[/align]
[align=left] [/align]
[align=left] return (std::back_insert_iterator<_Container>(_Cont));[/align]
[align=left] //返回一个插入迭代器[/align]
[align=left] [/align]
}

[align=left] [/align]
[align=left][/align]

[align=left]下面是back_inserter迭代器适配器生成的插入迭代器类back_insert_iterator源代码:[/align]
[align=left][/align]

// TEMPLATE CLASS back_insert_iterator
template<class _Container>
class back_insert_iterator
: public _Outit
{ // wrap pushes to back of container as output iterator
public:
typedef _Container container_type;
typedef typename _Container::reference reference;
typedef _Range_checked_iterator_tag _Checked_iterator_category;

explicit back_insert_iterator(_Container& _Cont)
: container(&_Cont)
{ // construct with container
}//构造函数,实参为被绑定的容器

back_insert_iterator<_Container>& operator=(//重载了赋值操作符
typename _Container::const_reference _Val)
{ // push value into container

container->push_back(_Val);//在这里,原来是调用了push_back函数
return (*this);

}

//解引、自增、自减都返回了插入迭代器本身的引用,但是这很关键
back_insert_iterator<_Container>& operator*()
{ // pretend to return designated value
return (*this);
}

back_insert_iterator<_Container>& operator++()
{ // pretend to preincrement
return (*this);
}

back_insert_iterator<_Container> operator++(int)
{ // pretend to postincrement
return (*this);
}

protected:
_Container *container; //保存了指向被绑定容器的指针
};

[align=left] [/align]
插入迭代器back_insert_iterator在概念上原来这么简单,不过是将重载了赋值操作符,然后在里面调用绑定容器的push_back操作完成真正的赋值。

虽然解引、自增、自减对于插入迭代器都没有实际意义,但这几个函数都被实现为返回自身的引用,这很关键,考虑在_Transforml函数中我们这样利用插入迭代器赋值:


*_Dest = _Func(*_First);//_Dest是一个插入迭代器


//如果*_Dest没有返回自身,那怎么能调用operator=()函数呢?

[align=left] [/align]
*_Dest返回类自身的引用能保证像上面的这种连贯调用而不出问题,自增、自减操作符返回自身的引用也是同样的道理。

同样,像back_inserter一样,front_inserter也是一个迭代器适配器:

// TEMPLATE FUNCTION front_inserter
template<class _Container> inline
front_insert_iterator<_Container> front_inserter(_Container& _Cont)
{ // return front_insert_iterator

return (std::front_insert_iterator<_Container>(_Cont));
//返回了front_insert_iterator
}

[align=left] [/align]
front_insert_iterator与back_insert_iterator几乎相同,只不过在重载的operator=()内调用的是被绑定容器的push_front操作而已。

[align=left]另一个更通用的插入迭代器适配器是inserter,用来生成在容器指定位置插入数据的插入迭代器。[/align]

// TEMPLATE FUNCTION inserter
template<class _Container,
class _Iter> inline
insert_iterator<_Container> inserter(_Container& _Cont, _Iter _Where)
{ // return insert_iterator

return (std::insert_iterator<_Container>(_Cont, _Where));
//返回insert_iterator对象
}

[align=left] [/align]
[align=left][/align]

这是insert_iterator:

// TEMPLATE CLASS insert_iterator
template<class _Container>
class insert_iterator
: public _Outit
{ // wrap inserts into container as output iterator

public:
//...
//构造函数
insert_iterator
(_Container& _Cont, typename _Container::iterator _Where)
: container(&_Cont), iter(_Where)
{ // construct with container and iterator
}

insert_iterator<_Container>& operator= //operator=()
(typename _Container::const_reference _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
};

//容器的insert函数在iter前面插入_Val,并返回新插入的元素迭代器,所以这里需要++iter使得iter重新指向初始位置
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: