C++ Primer 4 第九章 顺序容器

 

第九章 顺序容器

顺序容器内的元素按其位置存储和访问。标准库定义了三种顺序容器类型:vector、list 和 deque。它们的差别在于访问元素的方式,以及添加或删除元素相关操作的运行代价。标准库还提供了三种容器适配器。实际上,适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口,来适应基础的容器类型。顺序容器适配器包括 stack、queue 和 priority_queue 类型

1. 顺序容器的定义

容器构造函数:

         C<T> c;     创建一个名为 c 的空容器。C 是容器类型名,如 vector,T 是元素类型,如 int 或 string 适用于所有容器。

         C c(c2);      创建容器 c2 的副本 c;c 和 c2 必须具有相同的容器类型,并存放相同类型的元素。适用于所有容器。

         C c(b, e);    创建 c,其元素是迭代器 b 和 e 标示的范围内元素的副本。适用于所有容器。

         C c(n, t);     用 n 个值为 t 的元素创建容器 c,其中值 t 必须是容器类型 C 的元素类型的值,或者是可转换为该类型的值。只适用于顺序容器

         C c(n);        创建有 n 个值初始化(第 3.3.1 节)(value-initialized)元素的容器 c。只适用于顺序容器

将一个容器初始化为另一个容器的副本:当不使用默认构造函数,而是用其他构造函数初始化顺序容器时,必须指出该容器有多少个元素,并提供这些元素的初值。同时指定元素个数和初值的一个方法是将新创建的容器初始化为一个同类型的已存在容器的副本

         vector<int> ivec;

    vector<int> ivec2(ivec);   // ok: ivec is vector<int>

    list<int>   ilist(ivec);   // error: ivec is not list<int>

    vector<double> dvec(ivec); // error: ivec holds int not double

初始化为一段元素的副本:尽管不能直接将一种容器内的元素复制给另一种容器,但系统允许通过传递一对迭代器间接实现该实现该功能。使用迭代器时,不要求容器类型相同。容器内的元素类型也可以不相同,只要它们相互兼容,能够将要复制的元素转换为所构建的新容器的元素类型,即可实现复制。

迭代器标记了要复制的元素范围,这些元素用于初始化新容器的元素。迭代器标记出要复制的第一个元素和最后一个元素。采用这种初始化形式可复制不能直接复制的容器。更重要的是,可以实现复制其他容器的一个子序列:

     list<string> slist(svec.begin(), svec.end());

     vector<string>::iterator mid = svec.begin() + svec.size()/2;

     deque<string> front(svec.begin(), mid);

     deque<string> back(mid, svec.end());

指针就是迭代器,因此允许通过使用内置数组中的一对指针初始化容器:

     char *words[] = {"stately", "plump", "buck", "mulligan"};

     size_t words_size = sizeof(words)/sizeof(char *);

     list<string> words2(words, words + words_size);

         其中第二个指针提供停止复制的条件,其所指向的位置上存放的元素并没有复制。

分配和初始化指定数目的元素:创建顺序容器时,可显式指定容器大小和一个(可选的)元素初始化式。容器大小可以是常量或非常量表达式,元素初始化则必须是可用于初始化其元素类型的对象的值:

     const list<int>::size_type list_size = 64;

     list<string> slist(list_size, "eh?"); // 64 strings, each is eh?

不提供元素初始化式时,标准库将为该容器实现值初始化。采用这种类型的初始化,元素类型必须是内置或复合类型,或者是提供了默认构造函数的类类型。如果元素类型没有默认构造函数,则必须显式指定其元素初始化式。

         list<int> ilist(list_size); // 64 elements, each initialized to 0

         接受容器大小做形参的构造函数只适用于顺序容器,而关联容器不支持这种初始化。

容器内元素的类型约束:C++ 语言中,大多数类型都可用作容器的元素类型。容器元素类型必须满足以下两个约束:

         1)元素类型必须支持赋值运算。

         2)元素类型的对象必须可以复制。

         此外,关联容器的键类型还需满足其他的约束。

         大多数类型满足上述最低限度的元素类型要求。除了引用类型外,所有内置或复合类型都可用做元素类型。引用不支持一般意义的赋值运算,因此没有元素是引用类型的容器。IO 库类型不支持复制或赋值。因此,不能创建存放 IO 类型对象的容器。除输入输出(IO)标准库类型之外,所有其他标准库类型都是有效的容器元素类型。特别地,容器本身也满足上述要求,因此,可以定义元素本身就是容器类型的容器。

容器操作的特殊要求:支持复制和赋值功能是容器元素类型的最低要求。此外,一些容器操作对元素类型还有特殊要求。如果元素类型不支持这些特殊要求,则相关的容器操作就不能执行:我们可以定义该类型的容器,但不能使用某些特定的操作。例如,假设类 Foo 没有默认构造函数,但提供了需要一个 int 型形参的构造函数。现在,考虑下面的声明:

     vector<Foo> empty;     // ok: no need for element default constructor

     vector<Foo> bad(10);   // error: no default constructor for Foo

     vector<Foo> ok(10, 1); // ok: each element initialized to 1

         在描述容器操作时,我们应该留意(如果有的话)每个操作对元素类型的约束。

容器的容器:注意,在指定容器元素为容器类型时,必须如下使用空格:

     vector< vector<string> > lines; // ok: space required between close >

     vector< vector<string>> lines; // error: >> treated as shift operator

         必须用空格隔开两个相邻的 > 符号,以示这是两个分开的符号,否则,系统会认为 >> 是单个符号,为右移操作符,并导致编译时错误。

2. 迭代器和迭代器范围

每种容器类型都提供若干共同工作的迭代器类型。与容器类型一样,所有迭代器具有相同的接口:如果某种迭代器支持某种操作,那么支持这种操作的其他迭代器也会以相同的方式支持这种操作。

常用迭代器运算:

         *iter  返回迭代器 iter 所指向的元素的引用

        iter->mem对 iter 进行解引用,获取指定元素中名为 mem 的成员。等效于 (*iter).mem

        ++iter iter++给 iter 加 1,使其指向容器里的下一个元素

        --iter iter--给 iter 减 1,使其指向容器里的前一个元素

        iter1 == iter2  iter1 != iter2比较两个迭代器是否相等(或不等)。当两个迭代器指向同一个容器中的同一个元素,或者当它们都指向同一个容器的超出末端的下一位置时,两个迭代器相等

C++ 定义的容器类型中,只有 vector 和 deque 容器提供下面两种重要的运算集合:迭代器算术运算,以及使用除了 == 和 != 之外的关系操作符(>, >=, <, <=)来比较两个迭代器。这两种容器都支持通过元素位置实现的随机访问,因此它们的迭代器可以有效地实现算术和关系运算。

迭代器范围:C++ 语言使用一对迭代器标记迭代器范围(iterator range),这两个迭代器分别指向同一个容器中的两个元素或超出末端的下一位置,通常将它们命名为 first 和 last,或 beg 和 end,用于标记容器中的一段元素范围。

迭代器 first 和 last 如果满足以下条件,则可形成一个迭代器范围:

         1)它们指向同一个容器中的元素或超出末端的下一位置。

         2)如果这两个迭代器不相等,则对 first 反复做自增运算必须能够到达 last。换句话说,在容器中,last 绝对不能位于 first 之前。

         编译器自己不能保证上述要求。编译器无法知道迭代器所关联的是哪个容器,也不知道容器内有多少个元素。若不能满足上述要求,将导致运行时未定义的行为。

假设 first 和 last 标记了一个有效的迭代器范围,于是:

         1)当 first 与 last 相等时,迭代器范围为空;

         2)当 first 与不相等时,迭代器范围内至少有一个元素,而且 first 指向该区间中的第一元素。此外,通过若干次自增运算可以使 first 的值不断增大,直到 first == last 为止。

这两个性质意味着程序员可以安全地编写如下的循环,通过测试迭代器处理一段元素:

     while (first != last) {

         // safe to use *first because we know there is at least one element

         ++first;

     }

使迭代器失效的容器操作:任何指向已删除元素的迭代器都具有无效值,使用无效迭代器是没有定义的,可能会导致与悬垂指针相同的问题。无法检查迭代器是否有效,也无法通过测试来发现迭代器是否已经失效。使用迭代器时,通常可以编写程序使得要求迭代器有效的代码范围相对较短。然后,在该范围内,严格检查每一条语句,判断是否有元素添加或删除,从而相应地调整迭代器的值。

3.顺序容器的操作:

每种顺序容器都提供了一组有用的类型定义以及以下操作:

         1)在容器中添加元素。

         2)在容器中删除元素。

         3)设置容器大小。

         4)(如果有的话)获取容器内的第一个和最后一个元素。

容器定义的类型别名:

         size_type             无符号整型,足以存储此容器类型的最大可能容器长度

         iterator        此容器类型的迭代器类型

        const_iterator                元素的只读迭代器类型

        reverse_iterator    按逆序寻址元素的迭代器

        const_reverse_iterator   元素的只读(不能写)逆序迭代器

        difference_type    足够存储两个迭代器差值的有符号整型,可为负数

        value_type  元素类型

        reference       元素的左值类型,是 value_type& 的同义词

        const_reference   元素的常量左值类型,等效于 const value_type&

begin 和 end 成员: begin 和 end 操作产生指向容器内第一个元素和最后一个元素的下一位置的迭代器。这两个迭代器通常用于标记包含容器中所有元素的迭代器范围。如果容器是 const,则其返回类型要加上 const_ 前缀,也就是 const_iterator类型。

在顺序容器中添加元素的操作:

         c.push_back(t)     在容器 c 的尾部添加值为 t 的元素。返回 void 类型

        c.push_front(t)    在容器 c 的前端添加值为 t 的元素。返回 void 类型,只适用于 list 和 deque 容器类型.

        c.insert(p,t) 在迭代器 p 所指向的元素前面插入值为 t 的新元素。返回指向新添加元素的迭代器

        c.insert(p,n,t)       在迭代器 p 所指向的元素前面插入 n 个值为 t 的新元素。返回 void 类型

        c.insert(p,b,e)      在迭代器 p 所指向的元素前面插入由迭代器 b 和 e 标记的范围内的元素。返回 void 类型

容器元素都是副本: 在容器中添加元素时,系统是将元素值复制到容器里。类似地,使用一段元素初始化新容器时,新容器存放的是原始元素的副本。被复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会受到影响,反之亦然。

添加或删除 deque 或 vector 容器内的元素都会导致存储的迭代器失效:

vector<int>::iterator first = v.begin(),

                        last = v.end(); // cache end iterator

     // diaster: behavior of this loop is undefined

     while (first != last) {

         // do some processing

         // insert new value and reassign first, which otherwise would be invalid

         first = v.insert(first, 42);

         ++first;  // advance first just past the element we added

      }

         上述代码的行为未定义。在很多实现中,该段代码将导致死循环。问题在于这个程序将 end 操作返回的迭代器值存储在名为 last 的局部变量中。循环体中实现了元素的添加运算,添加元素会使得存储在 last 中的迭代器失效。该迭代器既没有指向容器 v 的元素,也不再指向 v 的超出末端的下一位置。

为了避免存储 end 迭代器,可以在每次做完插入运算后重新计算 end 迭代器值:

     // safer: recalculate end on each trip whenever the loop adds/erases elements

     while (first != v.end()) {

         // do some processing

         first = v.insert(first, 42); // insert new value

         ++first; // advance first just past the element we added

     }

关系操作符: 所有的容器类型都支持用关系操作符来实现两个容器的比较。比较的容器必须具有相同的容器类型,而且其元素类型也必须相同。容器的比较是基于容器内元素的比较。容器的比较使用了元素类型定义的同一个关系操作符:两个容器做 != 比较使用了其元素类型定义的 != 操作符。如果容器的元素类型不支持某种操作符,则该容器就不能做这种比较运算:

         1) 如果两个容器具有相同的长度而且所有元素都相等,那么这两个容器就相等;否则,它们就不相等。

         2) 如果两个容器的长度不相同,但较短的容器中所有元素都等于较长容器中对应的元素,则称较短的容器小于另一个容器。

         3) 如果两个容器都不是对文的初始子序列,则它们的比较结果取决于所比较的第一个不相等的元素。

容器大小的操作:

         c.size()       返回容器 c 中的元素个数。返回类型为 c::size_type

        c.max_size()        返回容器 c 可容纳的最多元素个数,返回类型为 c::size_type

        c.empty()    返回标记容器大小是否为 0 的布尔值

        c.resize(n) 调整容器 c 的长度大小,使其能容纳 n 个元素,如果 n < c.size(),则删除多出来的元素;否则,添加采用值初始化的新元素

        c.resize(n,t)          调整容器 c 的长度大小,使其能容纳 n 个元素。所有新添加的元素值都为 t

resize 操作可能会使迭代器失效。在 vector 或 deque 容器上做 resize 操作有可能会使其所有的迭代器都失效。对于所有的容器类型,如果 resize 操作压缩了容器,则指向已删除的元素迭代器失效。

访问元素:

         c.back()      返回容器 c 的最后一个元素的引用。如果 c 为空,则该操作未定义

        c.front()      返回容器 c 的第一个元素的引用。如果 c 为空,则该操作未定义

        c[n]   返回下标为 n 的元素的引用如果 n <0 或 n >= c.size(),则该操作未定义

                   只适用于 vector 和 deque 容器

        c.at(n) 返回下标为 n 的元素的引用。如果下标越界,则该操作未定义.只适用于 vector 和 deque 容器

         if (!ilist.empty()) {

         // val and val2 refer to the same element

         list<int>::reference val = *ilist.begin();

         list<int>::reference val2 = ilist.front();

 

         // last and last2 refer to the same element

         list<int>::reference last = *--ilist.end();

         list<int>::reference last2 = ilist.back(); }

删除元素:

         c.erase(p)   删除迭代器 p 所指向的元素.返回一个迭代器,它指向被删除元素后面的元素。如果 p 指向容器内的最后一个元素,则返回的迭代器指向容器的超出末端的下一位置。如果 p 本身就是指向超出末端的下一位置的迭代器,则该函数未定义

        c.erase(b,e) 删除迭代器 b 和 e 所标记的范围内所有的元素.返回一个迭代器,它指向被删除元素段后面的元素。如果 e 本身就是指向超出末端的下一位置的迭代器,则返回的迭代器也指向容器的超出末端的下一位置

        c.clear()      删除容器 c 内的所有元素。返回 void

        c.pop_back() 删除容器 c 的最后一个元素。返回 void。如果 c 为空容器,则该函数未定义

        c.pop_front() 删除容器 c 的第一个元素。返回 void。如果 c 为空容器,则该函数未定义            

                            只适用于 list 或 deque 容器

在删除元素之前,必须确保迭代器是不是 end 迭代器。使用 erase 操作删除单个必须确保元素确实存在——如果删除指向超出末端的下一位置的迭代器,那么 erase 操作的行为未定义。

erase、pop_front 和 pop_back 函数使指向被删除元素的所有迭代器失效。对于 vector 容器,指向删除点后面的元素的迭代器通常也会失效。而对于 deque 容器,如果删除时不包含第一个元素或最后一个元素,那么该 deque 容器相关的所有迭代器都会失效。

赋值:

         c1 = c2删除容器 c1 的所有元素,然后将 c2 的元素复制给 c1。c1 和 c2 的类型(包括容器类型和元素类型)必须相同赋值后,左右两边的容器相等:尽管赋值前两个容器的长度可能不相等,但赋值后两个容器都具有右操作数的长度。

         c1.swap(c2)         交换内容:调用完该函数后,c1 中存放的是 c2 原来的元素,c2 中存放的则是 c1 原来的元素。c1 和 c2 的类型必须相同。该函数的执行速度通常要比将 c2 复制到 c1 的操作快

        c.assign(b,e)        重新设置 c 的元素:将迭代器 b 和 e 标记的范围内所有的元素复制到 c 中。b 和 e 必须不是指向 c 中元素的迭代器

         c.assign(n,t)         将容器 c 重新设置为存储 n 个值为 t 的元素

赋值和 assign 操作使左操作数容器的所有迭代器失效。swap 操作则不会使迭代器失效。完成 swap 操作后,尽管被交换的元素已经存放在另一容器中,但迭代器仍然指向相同的元素。

如果在不同(或相同)类型的容器内,元素类型不相同但是相互兼容,则其赋值运算必须使用 assign 函数。assign 操作允许我们将一个容器的元素赋给另一个不同类型的容器。例如,可通过 assign 操作实现将 vector 容器中一段 char* 类型的元素赋给 string 类型 list 容器。

由于 assign 操作首先删除容器中原来存储的所有元素,因此,传递给 assign 函数的迭代器不能指向调用该函数的容器内的元素。

swap 操作实现交换两个容器内所有元素的功能。要交换的容器的类型必须匹配:操作数必须是相同类型的容器,而且所存储的元素类型也必须相同。调用了 swap 函数后,右操作数原来存储的元素被存放在左操作数中,反之亦然。该操作不会删除或插入任何元素,而且保证在常量时间内实现交换。由于容器内没有移动任何元素,因此迭代器不会失效。它们指向同一元素,就像没作 swap 运算之前一样。虽然,在 swap 运算后,这些元素已经被存储在不同的容器之中了。例如,在做 swap 运算之前,有一个迭代器 iter 指向 svec1[3] 字符串;实现 swap 运算后,该迭代器则指向 svec2[3] 字符串(这是同一个字符串,只是存储在不同的容器之中而已)。

4. vector 容器的自增长

为了支持快速的随机访问,vector 容器的元素以连续的方式存放——每一个元素都紧挨着前一个元素存储。为了使 vector 容器实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些。vector 容器预留了这些额外的存储区,用于存放新添加的元素。于是,不必为每个新元素重新分配容器。

capacity 和 reserve 成员: capacity 操作获取在容器需要分配更多的存储空间之前能够存储的元素总数,而 reserve 操作则告诉 vector 容器应该预留(即capacity的最小值)多少个元素的存储空间。每当 vector 容器不得不分配新的存储空间时,以加倍当前容量的分配策略实现重新分配。空 vector 容器的 size 是 0,而标准库显然将其 capacity 也设置为 0。当程序员在 vector 中插入元素时,容器的 size 就是所添加的元素个数,而其 capacity 则必须至少等于 size,但通常比 size 值更大。

5. 容器的选用

vector 和 deque 容器提供了对元素的快速随机访问,但付出的代价是,在容器的任意位置插入或删除元素,比在容器尾部插入和删除的开销更大。list 类型在任何位置都能快速插入和删除,但付出的代价是元素的随机访问开销较大。通常来说,除非找到选择使用其他容器的更好理由,否则 vector 容器都是最佳选择。

deque 容器同时提供了 list 和 vector 的一些性质:

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

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

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

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

一些选择容器类型的法则:

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

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

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

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

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

6. 再谈 string 类型

在某些方面,可将 string 类型视为字符容器。除了一些特殊操作,string 类型提供与 vector 容器相同的操作。string 类型与 vector 容器不同的是,它不支持以栈方式操纵容器:在 string 类型中不能使用 front、back 和 pop_back 操作。

string 类型提供容器操作意味着可将操纵 vector 对象的程序改写为操纵 string 对象。例如,以下程序使用迭代器将一个 string 对象的字符以每次一行的方式输出到标准输出设备:

     string s("Hiya!");

     string::iterator iter = s.begin();

     while (iter != s.end())

         cout << *iter++ << endl; // postfix increment: print old value

构造 string 对象的其他方法

         string s(cp, n) 创建一个 string 对象,它被初始化为 cp 所指向数组的前 n 个元素的副本

         string s(s2, pos2) 创建一个 string 对象,它被初始化为一个已存在的 string 对象 s2 中从下标 pos2 开始的字符的副本

         string s(s2, pos2, len2) 创建一个 string 对象,它被初始化为 s2 中从下标 pos2 开始的 len2 个字符的副本。如果 pos2 > s2.size(),则该操作未定义,无论 len2 的值是多少,最多只能复制 s2.size() - pos2 个字符.注意:n、len2 和 pos2 都是 unsigned 值

子串操作:

         s.substr(pos, n) 返回一个 string 类型的字符串,它包含 s 中从下标 pos 开始的 n 个字符

         s.substr(pos) 返回一个 string 类型的字符串,它包含从下标 pos 开始到 s 末尾的所有字符

         s.substr() 返回 s 的副本

修改 string 对象的操作:

         s.append( args) 将 args 串接在 s 后面。返回 s 引用

         s.replace(pos, len, args) 删除 s 中从下标 pos 开始的 len 个字符,用 args 指定的字符替换之。返回 s 的引用.在这个版本中,args 不能为 b2,e2

         s.replace(b, e, args) 删除迭代器 b 和 e 标记范围内所有的字符,用 args 替换之。返回 s 的引用.在这个版本中,args 不能为 s2,pos2,len2

string 类型的查找操作:

         s.find( args)         在 s 中查找 args 的第一次出现

        s.rfind( args)       在 s 中查找 args 的最后一次出现

        s.find_first_of( args) 在 s 中查找 args 的任意字符的第一次出现

        s.find_last_of( args) 在 s 中查找 args 的任意字符的最后一次出现

        s.find_first_not_of( args) 在 s 中查找第一个不属于 args 的字符

        s.find_last_not_of( args)        在 s 中查找最后一个不属于 args 的字符

string 类型 compare 操作:

         s.compare(s2)      比较 s 和 s2

        s.compare(pos1, n1, s2)         让 s 中从 pos 下标位置开始的 n1 个字符与 s2 做比较

        s.compare(pos1, n1, s2, pos2, n2)  让 s 中从 pos1 下标位置开始的 n1 个字符与 s2 中从 pos2 下标位置开始的 n2 个字符做比较

        s.compare(cp)      比较 s 和 cp 所指向的以空字符结束的字符串

        s.compare(pos1, n1, cp)         让 s 中从 pos1 下标位置开始的 n1 个字符与 cp 所指向的字符串做比较

        s.compare(pos1, n1, cp, n2)   让 s 中从 pos1 下标位置开始的 n1 个字符与 cp 所指向的字符串的前 n2 个字符做比较

7. 容器适配器

除了顺序容器,标准库还提供了三种顺序容器适配器:queue、priority_queue 和 stack。适配器(adaptor)是标准库中通用的概念,包括容器适配器、迭代器适配器和函数适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。例如,stack(栈)适配器可使任何一种顺序容器以栈的方式工作。

适配器通用的操作和类型:

         size_type    一种类型,足以存储此适配器类型最大对象的长度

         value_type 元素类型

        container_type 基础容器的类型,适配器在此容器类型上实现

         A a; 创建一个新空适配器,命名为 a

        A a(c); 创建一个名为 a 的新适配器,初始化为容器 c 的副本

         关系操作符:所有适配器都支持全部关系操作符:==、 !=、 <、 <=、 >、 >=

使用适配器时,必须包含相关的头文件:

     #include <stack>    // stack adaptor

     #include <queue>    // both queue and priority_queue adaptors

所有适配器都定义了两个构造函数:默认构造函数用于创建空对象,而带一个容器参数的构造函数将参数容器的副本作为其基础值。例如,假设 deq 是 deque<int> 类型的容器,则可用 deq 初始化一个新的栈,如下所示:

     stack<int> stk(deq);      // copies elements from deq into stk

覆盖基础容器类型: 默认的 stack 和 queue 都基于 deque 容器实现,而 priority_queue 则在 vector 容器上实现。在创建适配器时,通过将一个顺序容器指定为适配器的第二个类型实参,可覆盖其关联的基础容器类型:

     // empty stack implemented on top of vector

     stack< string, vector<string> > str_stk;

     // str_stk2 is implemented on top of vector and holds a copy of svec

     stack<string, vector<string> > str_stk2(svec);

对于给定的适配器,其关联的容器必须满足一定的约束条件。stack 适配器所关联的基础容器可以是任意一种顺序容器类型。因此,stack 栈可以建立在 vector、list 或者 deque 容器之上。而 queue 适配器要求其关联的基础容器必须提供 push_front 运算,因此只能建立在 list 容器上,而不能建立在 vector 容器上。priority_queue 适配器要求提供随机访问功能,因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器上。

两个相同类型的适配器可以做相等、不等、小于、大于、小于等于以及等于关系比较,只要基础元素类型支持等于和小于操作符既可。这些关系运算由元素依次比较来实现。第一对不相等的元素将决定两者之间的小于或大于关系。

栈容器适配器支持的操作:

         s.empty() 如果栈为空,则返回 true,否则返回 stack

        s.size() 返回栈中元素的个数

        s.pop() 删除栈顶元素的值,但不返回其值

        s.top() 返回栈顶元素的值,但不删除该元素

        s.push(item) 在栈顶压入新元素

尽管栈是以 deque 容器为基础实现的,但是程序员不能直接访问 deque 所提供的操作。例如,不能在栈上调用 push_back 函数,而是必须使用栈所提供的名为 push 的操作。

队列和优先级队列: priority_queue 允许用户为队列中存储的元素设置优先级。这种队列不是直接将新元素放置在队列尾部,而是放在比它优先级低的元素前面。标准库默认使用元素类型的 < 操作符来确定它们之间的优先级关系。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值