30.使用算法前确保目标区间足够大

问题实例

 
我们习惯了STL容器的自动扩张,因此可能会写下一些愚蠢的程序,举例而言:

1
2
3
4
5
6
int transmogrify(int x);//产生一个自变量是x的因变量
vector<int> values;
... // 把数据放入values
vector<int> results;
//对values中的所有元素使用函数,将结果插入res尾部
transform(values.begin(), values.end(),results.end(),transmogrify);

在本例中,transform调用了*res.end(),也就是说我们试图对一个不存在的对象赋值。


解决方案

插入迭代器

插入于结尾处

transform的结果放入结尾应该使用back_inserter来产生指向res末尾的迭代器

1
2
vector<int> results; 
transform(values.begin(), values.end(), back_inserter(results), transmogrify);

插入于最前端

如果你试图在最前端插入,当然可以用front_inserter,只是这样支持的容器只剩了list与deque.
如果依次添加到最前面,那么最终顺序与values对应的顺序不一致(正好相反),解决方法也简单,调用反向迭代器即可:

1
2
list<int> results;
transform(values.rbegin(), values.rend(),front_inserter(results), transmogrify);

在任意位置插入

inserter迭代器允许你强制算法把结果插入在任何位置:

1
2
vector<int> results;// results已经有一些数据
transform(values.begin(), values.end(), inserter(results, results.begin()+results.size()/2), transmogrify);

提高效率

每一次都只插入了一个对象,因此可能会造成线性表频繁的复制与扩容。对于复制没有什么优化思路,但至少可以使用reserve来减少再分配所带来的开销

1
2
3
4
5
6
7
vector<int> values; 
vector<int> results;
...
results.reserve(results.size() + values.size());
transform(values.begin(), values.end(),
inserter(results, results.begin() + results.size() / 2),
transmogrify); //无需扩容

替换而非插入

有时候我们要做的操作并不是插入而是替换,但这并不影响我们是否需要判断results的大小,解决方法要么resize,要么clear之后使用插入迭代器:

1
2
3
4
5
6
7
8
9
//resize法
if (results.size() < values.size()){ //确保大小
results.resize(values.size());
}
transform(values.begin(), values.end(),results.begin(), transmogrify);
//clear之后插入
results.clear();
results.reserve(values.size()); // 扩容优化
transform(values.begin(), values.end(),back_inserter(results),transmogrify);


总结

 
当我们使用一个要求指定目的区间的算法时,务必要确保目的区间已经足够大或者在算法执行时可以增加大小。如果需要增加大小,记得使用插入迭代器。