现在的位置: 首页 > 综合 > 正文

Effective STL 条款30

2018年12月13日 ⁄ 综合 ⁄ 共 12007字 ⁄ 字号 评论关闭

http://blog.csdn.net/amin2001/article/details/8063

STL容器在被添加时(通过insert、push_front、push_back等)自动扩展它们自己来容纳新对象。这工作的很好,有些程序员因为这个信仰而被麻痹,认为他们不必担心要为容器中的对象腾出空间,因为容器自己可以照顾好这些。如果是那样就好了!当程序员想向容器中插入对象但并没有告诉STL他们所想的时,问题出现了。这是一个常见的可以自我表现的方法:

int transmogrify(int x);     // 这个函数从x
       // 产生一些新值
vector<int> values;
...       // 把数据放入values
vector<int> results;     // 把transmogrify应用于
transform(values.begin(), values.end(),   // values中的每个对象
  results.end(),    // 把这个返回的values
  transmogrify);    // 附加到results
       // 这段代码有bug!

在本例中,transform被告知它的目的区间是从results.end()开始的,所以那就是开始写在values的每个元素上调用transmogrify的结果的地方。就像所有使用目标区间的算法,transform通过对目标区间的元素赋值的方法写入结果,transform会把transmogrify应用于values[0]并把结果赋给*results.end()。然后它会把transmogrify应用于value[1]并把结果赋给*(results.end()+1)。那只能带来灾难,因为在*results.end()没有对象,*(results.end()+1)也没有!调用transform是错误的,因为它会给不存在的对象赋值。(条款50解释了STL的一个调试实现怎么在运行期检测这个问题。)犯了这种错误的程序员几乎总是以为他们调用算法的结果能插入目标容器。如果那是你想要发生的,你就必须说出来。STL是一个库,不是一个精神。在本例中,说“请把transform的结果放入叫做results容器的结尾”的方式是调用back_inserter来产生指定目标区间起点的迭代器:

vector<int> results;     // 把transmogrify应用于
transform(values.begin(), values.end(),   // values中的每个对象,
   back_inserter(results),  // 在results的结尾
   transmogrify);   // 插入返回的values

在内部,back_inserter返回的迭代器会调用push_back,所以你可以在任何提供push_back的容器上使用back_inserter(也就是任何标准序列容器:vector、string、deque和list)。如果你想让一个算法在容器的前端插入东西,你可以使用front_inserter。在内部,front_inserter利用了push_front,所以front_insert只和提供那个成员函数的容器配合(也就是deque和list):

...       // 同上
list<int> results;      // results现在是list
transform(values.begin(), values.end(),   // 在results前端
   front_inserter(results),  // 以反序
   transmogrify);   // 插入transform的结果

因为front_inserter用push_front把每个对象添加到results,results中的对象顺序会和values中对应的对象顺序相反。这也是为什么front_inserter没有back_inserter那么常用的原因之一。另一个原因是vector不提供push_front,所以front_inserter不能用于vector。

如果你要transform把输出结果放在results前端,但你也要输出和values中对应的对象顺序相同,只要以相反的顺序迭代values:

list<int> results;      // 同上
transform(values.rbegin(), values.rend(),   // 在results前端
   front_inserter(results),  // 插入transform的结果
   transmogrify);   // 保持相对的对象顺序

front_inserter让你强制算法在容器前端插入它们的结果,back_inserter让你告诉它们把结果放在容器后端,有点惊人的是inserter允许你强制算法把它们的结果插入容器中的任意位置:

vector<int> values;     // 同上
...
vector<int> results;     // 同上,除了现在
...       // 在调用transform前
       // results已经有一些数据
transform(values.begin(), values.end(),   // 把transmogrify的
 inserter(results, results.begin() + results.size()/2), // 结果插入
 transmogrify);     // results的中间

不管你是否使用了back_inserter、front_inserter或inserter,每次对目的区间的插入只完成一个对象。条款5解释了对于连续内存容器(vector、string和deque)来说这可能很昂贵,但条款5的建议解决方法(使用区间成员函数)不能应用于使用算法来完成插入的情况。在本例中,transform会对目的区间每次写入一个值,你无法改变。

当你要插入的容器是vector或string时,你可以通过按照条款14的建议最小化这个代价,预先调用reserve。你仍然要承受每次发生插入时移动元素的开销,但至少你避免了重新分配容器的内在内存:

vector<int> values;     // 同上
vector<int> results;
...
results.reserve(results.size() + values.size());  // 确定results至少
       // 还能装得下
       // values.size()个元素
transform(values.begin(), values.end(),   // 同上,
 inserter(results, results.begin() + results.size() / 2),// 但results
 transmogrify);     // 没有任何重新分配操作

当使用reserve来提高一连串插入的效率时,总是应该记住reserve只增加容器的容量:容器的大小仍然没有改变。即使调用完reserve,当你想要让容器把新元素加入到vector或string时,你也必须对算法使用插入迭代器(比如,从back_inserter、front_inserter或inserter返回的迭代器之一)。

要把这些完全弄清楚,这里有一个提高本条款开始时的那个例子的效率的错误方法(就是我们把transmogrify作用于values里的数据的结果附加到results的那个例子):

vector<int> values;    // 同上
vector<int> results;
...
results.reserve(results.size() + values.size()); // 同上
transform(values.begin(), values.end(),  // 写入transmogrify的结果
  results.end(),   // 到未初始化的内存
  transmogrify);   // 行为未定义!

在这段代码中,transform愉快地试图对results尾部的原始的、未初始化的内存赋值。通常,这会造成运行期错误,因为赋值只在两个对象之间操作时有意义,而不是在一个对象和一块原始的比特之间。即使这段代码碰巧作了你想要它做的事情,results也不会知道transform在它的未使用容量上“创造”的新“对象”。直到results知道之前,它的大小在调用transform后仍然和原来一样。同样的,它的end迭代器仍和调用transform前指向同样的位置。结论呢?使用reserve而没有用插入迭代器会在算法内部导致未定义行为,也会弄乱容器。

正确地写这个例子的代码的方法是使用reserve和插入迭代器:

vector<int> values;    // 同上
vector<int> results;
results.reserve(results.size() + values.size()); // 同上
transform(values.begin(), values.end(),  // 把transmogrify的结果
  back_inserter(results),  // 写入results的结尾,
  transmogrify);   // 处理时避免了重新分配

到目前为止,我都在假设你让像transform那样的算法把它们的结果作为新元素插入容器。这是通常的期望,但有时候你要覆盖现有容器的元素而不是插入新的。当这种情况时,你不需要插入迭代器,但你仍然需要按照本条款的建议来确保你的目的区间足够大。

比如,假设你让transform覆盖results的元素。如果results至少有和values一样多的元素,那很简单。如果没有,你也必须使用resize来确保它有。

vector<int> values;
vector<int> results;
...
if (results.size() < values.size()){  // 确保results至少
 results.resize(values.size());  // 和values一样大
}
transform(values.begin(), values.end(),  // 覆盖values.size()个
  results.begin(),   // results的元素
  transmogrify);

或者你可以清空results然后用通常的方式使用插入迭代器:

...
results.clear();     // 销毁results中
      // 的所有元素
results.reserve(values.size());   // 保留足够空间
transform(values.begin(), values.end(),  // 把transform地返回值
  pack_inserter(results),  // 放入results
  transmogrify);

本条款论证了这个主题的很多变化,但我希望你能牢牢记住本质。无论何时你使用一个要求指定目的区间的算法,确保目的区间已经足够大或者在算法执行时可以增加大小。如果你选择增加大小,就使用插入迭代器,比如ostream_iterators或从back_inserter、front_inserter或inserter返回的迭代器。这是所有你需要记住的东西。

注意:

copy(ve.begin(),ve.end(),back_inserter(v1));是将ve的数据插入到v1的后面

一:函数模板类

    1、一元函数原型

      template<class _A,class _R>

      struct unary_function

      {

            typedef _A argument_type;

            typedef _R result_type;

      };

    2、二元函数原型

      template<class Arg1,class Arg2,class Result>

      struct unary_function

      {

            typedef Arg1 first_argument_type;

            typedef Arg2 Second_argument_type;

            typedef Result result_type;

      };

    3、系统函数对象

      plus             返回两个数的和:a+b    

      minus            返回两个数的差:a-b

      multiplies       返回两个数的乘积:a*b

      divides          返回两个数的商:a/b

      mudulus          返回两个数的模:a%b

      negate           返回某个数的相反数:-a

     

      equal_to         判断两个数是否相等:a==b

      not_equal_to     判断两个数是否不等:a!=b

      greater          判断第一个数是否大于第二个数:a>b

      less             判断第一个数是否小于第二个数:a<b

      greate_equal     判断第一个数是否大于等于第二个数:a>=b

      less_equal       判断第一个数是否小于等于第二个数:a<=b

 

      logical_and      返回两个数的逻辑与结果:a&&b

      logical_or       返回两个数的逻辑或结果:a||b

      logical_not      返回某个数的逻辑非结果:!a

 

二:通用容器

    1、vector

      vector/vector(int nSize)      创建向量容器

      assign                        对Vector中的元素赋值

      at                            返回指定位置的元素

      back                          返回最末一个元素

      begin                         返回第一个元素的迭代器

      capacity                      返回vector所能容纳的元素数量(在不重新分配内存的情况下)

      clear                         清空所有元素

      empty                         判断Vector是否为空(返回true时为空)

      end                           返回最末元素的迭代器(译注:实指向最末元素的下一个位置)

      erase                         删除指定元素

      front                         返回第一个元素

      get_allocator                 返回vector的内存分配器

      insert                        插入元素到Vector中

      max_size                      返回Vector所能容纳元素的最大数量(上限)

      pop_back                      移除最后一个元素

      push_back                     在Vector最后添加一个元素

      rbegin                        返回Vector尾部的逆迭代器

      rend                          返回Vector起始的逆迭代器

      reserve                       设置Vector最小的元素容纳数量

      resize                        改变Vector元素数量的大小

      size                          返回Vector元素数量的大小

      swap                          交换两个Vector

 

    2、list

      assign                        给list赋值

      back                          返回最后一个元素

      begin                         返回指向第一个元素的迭代器

      clear                         删除所有元素

      empty                         如果list是空的则返回true

      end                           返回末尾的迭代器

      erase                         删除一个元素

      front                         返回第一个元素

      get_allocator                 返回list的配置器

      insert                        插入一个元素到list中

      max_size                      返回list能容纳的最大元素数量

      merge                         合并两个list

      pop_back                      删除最后一个元素

      pop_front                     删除第一个元素

      push_back                     在list的末尾添加一个元素

      push_front                    在list的头部添加一个元素

      rbegin                        返回指向第一个元素的逆向迭代器

      remove                        从list删除元素

      remove_if                     按指定条件删除元素

      rend                          指向list末尾的逆向迭代器

      resize                        改变list的大小

      reverse                       把list的元素倒转

      size                          返回list中的元素个数 

      sort                          给list排序

      splice                        合并两个list

      swap                          交换两个list

      unique                        删除list中重复的元素

    3、deque

      push_front                      头部添加一个元素

      push_back                       尾部添加一个元素

      insert                          插入一个元素

      erase                           删除一个元素

      pop_front                       删除最前一个元素

      pop_back                        删除最后一个元素

      clear                           清楚所有元素

      at                              返回指定位置的元素

    4、stack

      stack<class T,class Container = deque/vector/list<T>>   构造栈

      empty                                                 堆栈为空则返回真

      pop                                                   移除栈顶元素

      push                                                  在栈顶增加元素

      size                                                  返回栈中元素数目

      top                                                   返回栈顶元素

    5、queue

      queue<class T,class Container = deque/list<T>>          构造队列

      back                                                  返回最后一个元素

      empty                                                 如果队列空则返回真

      front                                                 返回第一个元素

      pop                                                   删除第一个元素

      push                                                  在末尾加入一个元素

      size                                                  返回队列中元素的个数

    6、set

      begin                指向set的头指针

      clear                删除所有元素

      count                返回某键值元素的个数

      empty                返回是否为空

      end                  指向set的尾指针

      erase                删除某位置元素

      pair<iterator, iterator>equal_range(const key_type& x)         返回一个迭代器对(指向键不小于x的第一个元素的迭代器,指向键大于x的第一个元素的迭代器)

      find                 返回某索引元素指针

      get_allocator        返回构造函数的一个拷贝

      pair<iterator,bool>insert(const value_type& x)返回<指向元素x的迭代器,是否插入成功>

      lower_bound          返回键值不小于某值的第一个元素的迭代器

      max_size             返回该set可以控制的最大长度

      rbegin               返回反向set的反向头指针

      rend                 返回反响set的反向尾指针

      resize(size_type Sz, T C=T())                                  插入或删除使元素的个数为n,插入的元素为C

    7、map

      map(const Pred& comp=Pred(),const A& al=A())                           构造函数

      pair<const_iterator,const_iterator>equal_range(const Key& key)const:   返回迭代器中键值等于key的迭代指针[fitst,last)

    8、迭代器函数

      back_inserter        返回容器的后插迭代器

      front_inserter       返回容器的前插迭代器

三:非变异算法

   

    1、循环

      for_each               对每个元素执行相同的函数操作

   

    2、查询

      find                   某值第一次出现位置

      find_if                符合某谓词的第一个元素

      find_first_of          子序列某元素第一次出现位置

      adjacent_find          第一次相邻元素相等的位置

      find_end               子序列最后出现位置

      search                 子序列第一次出现位置

      search_n               一个值连续出现n次的位置

    3、计数

      count                  某值出现的此时

      count_if               与某谓词匹配的次数

    4、比较

      equal                  两个序列对应元素都相同为真

      mismatch               两个序列相异的第一个元素

四:变异算法

    1、变换

      transform              将某操作应用于指定范围的元素

    2、填充

      fill                   用给定值填充所有元素

    3、生成

      generate               用一操作的结果填充所有元素

    4、唯一

      unique/unique_copy     删除相邻位置重复的元素

    5、反转

      reverse/reverse_copy   循环移动元素

    6、随机

      random_shuffle         随机移动元素

    7、划分

      partition/stable_paritition 满足谓词的元素都放在前面

五:排序及相关操作

    1、排序

      sort                         以很好的平均效率排序

      stable_sort                  稳定排序

      partial_sort                 局部排序

      partial_sort_copy            复制时进行局部排序

    2、二分检索

      lower_bound                  大于等于某值第一次出现的迭代器位置

      upper_bound                  大于某值第一次出现位置

      equal_range                  等于某值迭代器范围

      binary_search                是否存在某值

    3、归并

      merge                        归并两个迭代器有序序列

      inplace_merge                归并单个迭代器有序序列

    4、有序结构上的集合操作

      includes                     一个序列为另一个序列子集为真

      set_union                    构造两集合有序并集

      set_intersection             构造有序交集

      set_difference               构造有序差集

      set_symmetric_difference     构造有序对称差

    5、堆操作

      make_heap                    从序列构造堆

      pop_heap                     从堆中弹出元素

      push_heap                    加入元素

      sort_heap                    排序

    6、最大最小

      max

      min

      min_element

      max_element

    7、词典比较

      lexicographical_compare      两个序列按字典序排序

    8、排列生成器

      next_permutation             按字典序的下一个排序

      prev_permutation             按字典序的上一个排序

    9、数值算法

      accumulate                   累积求和

      inner_product                内积求和

      partial_sum                  创建新序列,每个元素值代表指定范围内该位置前所有元素之和

      adjacent_difference          相邻元素差集

http://www.crackerban.com/?p=1158

【上篇】
【下篇】

抱歉!评论已关闭.