Effective STL [30] | 确保目标区间足够大
STL容器在被添加时(通过insert、push_front、push_back等)自动扩展它们自己来容纳新对象。
插入数据
尾部插入 back_inserter
当你想向容器中插入对象但并没有告诉STL他们所想的时,问题出现了:
|
|
transform被告知它的目的区间是从results.end()
开始的,所以那就是开始写在values的每个元素上调用transmogrify
的结果的地方。
就像所有使用目标区间的算法,transform
通过对目标区间的元素赋值的方法写入结果,transform
会把transmogrify
应用于values[0]
并把结果赋给*results.end()
。
然后它会把transmogrify
应用于value[1]
并把结果赋给*(results.end()+1)
。
那只能带来灾难,因为在*results.end()
没有对象,*(results.end()+1)
也没有!因为transform
并没有在尾部创造新的对象。
调用transform
是错误的,因为它会给不存在的对象赋值。
正确做法
把transform
的结果放入results
容器的结尾的方式是调用back_inserter
来产生指定目标区间起点的迭代器:
|
|
在内部,back_inserter
返回的迭代器会调用push_back
,所以你可以在任何提供push_back
的容器上使用back_inserter
(也就是任何标准序列容器: vector
、string
、deque
和list
)。
前端插入 front_inserter
如果你想让一个算法在容器的前端插入东西,你可以使用front_inserter
。
在内部,front_inserter
利用了push_front
,所以front_insert
只和提供那个成员函数的容器配合(也就是deque
和list
):
|
|
因为front_inserter
用push_front
把每个对象添加到results
,results
中的对象顺序会和values
中对应的对象顺序相反。
vector
不提供push_front
,所以front_inserter
不能用于vector
。
同序插入
如果你要transform
把输出结果放在results
前端,但你也要输出和values
中对应的对象顺序相同,只要以相反的顺序迭代values
:
|
|
任意位置插入 inserter
front_inserter
让你强制算法在容器前端插入它们的结果,back_inserter
让你告诉它们把结果放在容器后端,有点惊人的是inserter
允许你强制算法把它们的结果插入容器中的任意位置:
|
|
插入效率
不管你是否使用了back_inserter
、front_inserter
或inserter
,transform
会对目的区间每次写入一个值,你无法改变。
当你要插入的容器是vector
或string
时,你可以最小化这个代价,预先调用reserve
。
你仍然要承受每次发生插入时移动元素的开销,但至少你避免了重新分配容器的内在内存:
|
|
当使用reserve
来提高一连串插入的效率时,总是应该记住reserve
只增加容器的容量:容器的大小仍然没有改变。
即使调用完reserve
,当你想要让容器把新元素加入到vector
或string
时,你也必须对算法使用插入迭代器(比如,从back_inserter
、front_inserter
或inserter
返回的迭代器之一),因为赋值只在两个对象之间操作时有意义,而不是在一个对象和一块原始的比特之间。
第一个例子正确的写法:
|
|
覆盖原始数据
有时候你要覆盖现有容器的元素而不是插入新的。
当这种情况时,你不需要插入迭代器,但你仍然需要按照本条款的建议来确保你的目的区间足够大。
假设你让transform
覆盖results
的元素。如果results
至少有和values
一样多的元素,那很简单。如果没有, 你也必须使用resize
来确保它有。
|
|
或者你可以清空results然后用通常的方式使用插入迭代器:
|
|
结论
无论何时你使用一个要求指定目的区间的算法,确保目的区间已经足够大或者在算法执行时可以增加大小。
如果你选择增加大小,就使用插入迭代器,比如ostream_iterators
或从back_inserter
、front_inserter
或inserter
返回的迭代器。