Effective STL第1条:慎重选择容器类型

一、STL的容器概述

  • 标准STL序列容器:vector、string、deque和list
  • 标准STL关联容器:set、multiset、map、和multimap
  • 非标准序列容器slist和rope:slist是一个单向链表,rope本质上是一“重型”string
  • 非标准关联容器:hash_set、hash_multiset、hash_map和hash_multimap
  • vector<char>作为string的替代:第13条将要讲述这种替代是有意义的
  • vector作为标准关联容器的替代:第23条中将要阐明,有时vector在运行时间和空间上都要优于标准关联容器
  • 几种标准的非STL容器:包括数组、bitset、valarry、stack、queue和priority_queue

二、容器的选择

  • C++标准就“如何在vector、deque、list中做出选择”提供了如下的建议:
    • vector、deque、list为程序员提供了不同的复杂性,使用时要对此做出权衡
    • vector是默认应使用的序列类型
    • 当需要频繁地在序列中间做插入和删除操作时,应使用list
    • 当大多数插入和删除操作发生在序列的头部和尾部时,deque是应该考虑的数据结构

三、连续内存容器、基于节点的容器

  • 连续内存容器:
    • 把它的元素存放在一块或多块(动态分配的)内存中,每块内存中有多个元素
    • 当有新元素插入或已有的元素被删除时,同一内存中的其它元素要向前或向后移动,以便为新元素让出空间,或者填充被删除元素所留下的空隙。这种移动操作影响到效率(见第5、6条)和异常安全性
    • 标准的连续内存容器有vector、deque、string。非标准的rope也是一个连续内存容器
  • 基于节点的容器:
    • 每一个(动态分配的)内存块只存放一个元素
    • 容器中元素的插入或删除只影响指向节点的指针,而不影响节点本身的内容,所以当有插入或删除操作时,元素的值不需要移动
    • 表示链表的容器:list、slist,是基于节点的;所有的关联容器也是如此(通常的实现方式是平衡树);非标准的哈希容器使用不同的基于节点的实现(在第25条中介绍)

四、选择容器时需要考虑的因素与问题

  • 你是否需要在容器的任意位置插入新元素?如果需要,就选择序列容器;关联容器是不行的。
  • 你是否关心容器中的元素是排序的?如果不关心,则散列容器是一个可行的选择方案;否则,你要避免散列容器。
  • 你选择的容器必须是标准C++的一部分吗?如果必须是,就排除了散列的容器,slist和rope。
  • 你需要哪种类型的迭代器?如果它们必须是随机访问迭代器,则对容器的选择就被限定为vector、deque和string。或许你也可以考虑一下rope(见第50条)。如果要求使用双向迭代器,那么你要避免slist(见第50条)以及哈希容器的一个常见实现(见第25条)
  • 当发生元素的插入或删除操作时,避免移动容器中原来的元素是否很重要?如果是,就要避免连续内存容器。(见第5条)
  • 容器中数据的布局是否需要和C兼容?如果需要兼容,就要只能选择vector。(见第16条)
  • 元素的查找速度是否是关键的考虑因素?如果是,就要考虑哈希容器(见第25条)、排序的vector(见23条)和标准关联容器——或许这就是优先顺序。
  • 如果容器内部使用了引用计数技术(reference counting),你是否介意?如果是,就要避免使用string,因为许多string的实现都使用了引用计数。Rope也要避免,因为权威的rope实现是基于引用计数的(见第50条)。当然,你需要某种表示字符串的方法,这时你可以考虑vector<char>。
  • 对插入和删除操作,你需要事务语义(transaction semantics)吗?也就是说,在插入和删除操作失败时,你需要回滚的能力吗?如果需要,你就要使用基于节点的容器。如果对多个元素的插入操作(即针对一个区间的形式——见第5条)需要事务,则你需要选择list,因为在标准容器中,只有list对多个元素的插入操作提供了事务语义。对那些希望编写异常安全(exception-safe)代码的程序员,事务语义显得尤为重要。(使用连续内存的容器也可以获得事务语义,但是要付出性能上的代价,而且代码也显得不那么直截了当。
  • 你需要使迭代器、指针和引用变为无效的次数最少吗?如果是这样,就要使用基于节点的容器,因为对这类容器的插入和删除操作从来不会使迭代器、指针和引用变为无效(除非他们指向了一个你正在删除的元素)。而针对连续内存容器的插入和删除操作一般会使指向该容器的迭代器、指针、和引用变为无效。
  • 如果序列容器的迭代器是随机访问类型,而且只要没有删除操作发生,且插入操作只发生在容器的末尾,则指向数据的指针和引用就不会变为无效这样的容器是否对你有帮助吗?这是非常特殊的情形,但如果你面对的情形正是如此,则deque是你所希望的容器。(有意思的是,当插入操作仅在容器的末尾发生时,deque的迭代器有可能会变为无效。deque是唯一的、迭代器可能会变为无效,而指针和引用不会变为无效的STL标准容器。)
发布了1300 篇原创文章 · 获赞 827 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/103944370