C++ Primer 学习笔记15 容器的选择

来源:互联网 发布:医疗系统php源代码 编辑:程序博客网 时间:2024/06/08 18:10

为了支持快速的随机访问,vector容器的元素以连续的方式存放——每一个元素都紧挨着前一个元素存储。

为了使 vector 容器实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些。vector 容器预留了这些额外的存储区,用于存放新添加的元素。

vector 类提供了两个成员函数:capacity 和 reserve 使程序员可与 vector 容器内存分配的实现部分交互工作。capacity操作获取在容器需要分配更多的存储空间之前能够存储的元素总数,而 reserve操作则告诉 vector 容器应该预留多少个元素的存储空间。

size 指容器当前拥有的元素个数;而 capacity 则指容器在必须分配新存储空间之前可以存储的元素总数。

为了说明 size 和 capacity 的交互作用,考虑下面的程序:

#include <iostream>#include <vector>using namespace std;int _tmain(int argc, _TCHAR* argv[]){     vector<int> ivec;       // size should be zero; capacity is implementation defined      cout << "ivec: size: " << ivec.size()           << " capacity: "  << ivec.capacity() << endl;       // give ivec 24 elements      for (vector<int>::size_type ix = 0; ix != 24; ++ix)           ivec.push_back(ix);  // size should be 24; capacity will be >= 24 and is implementation defined      cout << "ivec: size: " << ivec.size()           << " capacity: "  << ivec.capacity() << endl;    system ("pause");return 0;}

运行该程序时,得到以下输出结果:

     ivec: size: 0 capacity: 0

     ivec: size: 24 capacity: 28

由此可见,空 vector 容器的 size 是 0,而标准库显然将其 capacity 也设置为 0。当程序员在 vector 中插入元素时,容器的 size 就是所添加的元素个数,而其 capacity 则必须至少等于 size,但通常比 size 值更大。在上述程序中,一次添加一个元素,共添加了 24 个元素,结果其 capacity 为 28。 容器的当前状态如下图所示:


下面的程序将预留的容量用完:

 // add elements to use up the excess capacity      while (ivec.size() != ivec.capacity())           ivec.push_back(0);      // size should be 28; capacity should be unchanged      cout << "ivec: size: " << ivec.size()       << " capacity: "  << ivec.capacity() << endl;

由于在该程序中,只使用了预留的容量,因此 vector 不必做任何的内存分配工作。事实上,只要有剩余的容量,vector 就不必为其元素重新分配存储空间。

此时我们已经耗尽了预留的容量,该容器的 size 和 capacity 值相等:

     ivec: size: 28 capacity: 28

此时,如果要添加新的元素,vector必须为自己重新分配存储空间:

 ivec.push_back(42); // add one more element         cout << "ivec: size: " << ivec.size()           << " capacity: "  << ivec.capacity() << endl; 

这段程序的输出:

     ivec: size: 29 capacity: 42

表明:每当 vector 容器不得不分配新的存储空间时,以加倍当前容量的分配策略实现重新分配。

容器的选用

    元素是否连续存储还会显著地影响:

•  在容器的中间位置添加或删除元素的代价。

•  执行容器元素的随机访问的代价。

vector 和 deque容器提供了对元素的快速随机访问,但付出的代价是,在容器的任意位置插入或删除元素,比在容器尾部插入和删除的开销更大。list 类型在任何位置都能快速插入和删除,但付出的代价是元素的随机访问开销较大。

插入操作如何影响容器的选择

list 容器表示不连续的内存区域,在任何位置都可高效地 insert 或 erase 一个元素。插入或删除 list 容器中的一个元素不需要移动任何其他元素。另一方面,list 容器不支持随机访问,访问某个元素要求遍历涉及的其他元素。

对于 vector 容器,除了容器尾部外,其他任何位置上的插入(或删除)操作都要求移动被插入(或删除)元素右边所有的元素。

deque 容器拥有更加复杂的数据结构。从 deque 队列的两端插入和删除元素都非常快。在容器中间插入或删除付出的代价将更高。 deque 容器同时提供了 list 和 vector 的一些性质:

•  与 vector 容器一样,在 deque 容器的中间 insert 或 erase 元素效率比较低。

•  不同于 vector 容器,deque 容器提供高效地在其首部实现insert 和 erase 的操作,就像在容器尾部的一样。

•  与 vector 容器一样而不同于 list容器的是, deque 容器支持对所有元素的随机访问。

•  在 deque 容器首部或尾部插入元素不会使任何迭代器失效,而首部或尾部删除元素则只会使指向被删除元素的迭代器失效。在 deque 容器的任何其他位置的插入和删除操作将使指向该容器元素的所有迭代器都失效。

元素的访问如何影响容器的选择

vector 和 deque 容器都支持对其元素实现高效的随机访问。也就是说,我们可以高效地先访问 5 号元素,然后访问 15 号元素,接着访问 7 号元素,等等。由于 vector 容器的每次访问都是距离其起点的固定偏移,因此其随机访问非常有效率。在 list 容器中,上述跳跃访问会变得慢很多。在 list 容器的元素之间移动的唯一方法是顺序跟随指针。从 5 号元素移动到 15 号元素必须遍历它们之间所有的元素。

通常来说,除非找到选择使用其他容器的更好理由,否则 vector 容器都是最佳选择。

选择容器的提示

下面列举了一些选择容器类型的法则:

1.  如果程序要求随机访问元素,则应使用 vector 或 deque 容器。

2.  如果程序必须在容器的中间位置插入或删除元素,则应采用 list 容器。

3.  如果程序不是在容器的中间位置,而是在容器首部或尾部插入或删除元素,则应采用 deque 容器。

4.  如果只需在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑在输入时将元素读入到一个 list 容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的 list 容器复制到一个 vector容器。

如果无法确定某种应用应该采用哪种容器,则编写代码时尝试只使用 vector 和 lists 容器都提供的操作:使用迭代器,而不是下标,并且避免随机访问元素。这样编写,在必要时,可很方便地将程序从使用 vector 容器修改为使用 list 的容器。





0 0
原创粉丝点击