【C++笔记】9. 顺序容器

9. 顺序容器

  1. 一个容器就是一些特定类型对象的集合。

  2. 顺序容器为程序员提供了控制元素存储和访问顺序的能力,这种能力不依赖于元素的值,而是与元素加入容器时的位置相对应。

  3. 第11章将介绍有序和无需关联容器,根据关键字的值来存储元素。

  4. 标准库还提供了三种容器适配器,分别为容器操作定义了不同的接口,来与容器适配。

9.1 顺序容器概述

  1. 顺序容器类型:

    • vector:可变大小数组
      优点:支持快速随机访问,尾部插入/删除速度快
      缺点:在尾部之外的位置插入或删除元素可能很慢
    • string:与vector相似,但仅用于字符
      优点:支持快速随机访问,尾部插入/删除速度快
      缺点:在尾部之外的位置插入或删除元素可能很慢
    • deque:双端队列
      优点:支持快速随机访问,在头尾位置插入/删除元素很快
      缺点:在尾部之外的位置插入或删除元素可能很慢
    • list:双向链表
      优点:在任何位置进行插入/删除操作速度都很快
      缺点:不支持随机访问,只支持双向顺序访问,占用内存大
    • forward_list:单向链表,且没有size操作
      优点:在任何位置进行插入/删除操作速度都很快
      缺点:不支持随机访问,只支持单向顺序访问,占用内存大
    • array:固定大小数组
      优点:支持快速随机访问
      缺点:不能添加或删除元素
  2. 确定使用哪种容器:

    • 除非有很好的理由使用其他容器,否则应使用vector。
    • 如果需要随机访问元素,应使用vector或deque。
    • 如果需要容器中间插入或删除元素,应使用list或forward_list。
    • 如果程序有很多小元素,且空间的额外开销很重要,则不要使用list和forward_list。
    • 如果需要在头尾位置插入或删除元素,但不会在中间位置进行插入或删除操作,则使用deque。
    • 如果程序只有在读取输入时才需要在容器中间位置添加元素,随后需要随机访问元素,则:
      • 首先,确定是否真的需要在容器中间位置添加元素。当处理输入数据时,通常可以很容易地向vector追加数据,然后再调用标准库的sort函数来重排容器中的元素,从而避免在中间位置添加元素。
      • 如果必须在中间位置添加元素,考虑在输入阶段使用list,一旦输入完成,将list中的内容拷贝到一个vector中。
    • 如果程序既需要随机访问元素,又需要在容器中间插入元素,则根据实际情况分析哪种情况更多,最后根据性能要求选择。
  3. 如果不确定使用哪种容器,可以在程序中只使用vector和list公共的操作:迭代器(而非下标操作),这样必要时随时可以替换。

9.2 容器库概览

  1. 一般情况下每个容器都定义在一个头文件中,文件名与类型名相同。

  2. 容器的容器,较老的编译器需要两个尖括号中加入空格:
    vector<vector<string> >

  3. 如果容器中的元素类型没有默认构造函数,则我们应在构造容器时进行初始化。

  4. 容器操作表(已省略后面重点介绍的)

    操作种类类型别名说明
    类型size_type无符号整型,足够表示最大可能容器的大小
    类型difference_type带符号整型,足够保存两个迭代器之间的距离
    类型value_type元素类型
    大小c.size()C中的元素数目(不支持forward_list)
    大小c.max_size()c可保存的最大元素数目
    大小e.empty()若c中存储了元素,返回false

9.2.1 迭代器

  1. forward_list迭代器不支持递减运算符(–)。
    迭代器支持的算术运算(+1,+=1,<,>)只支持string, vector, deque, array。

  2. 迭代器范围:由一对迭代器表示,两个迭代器分别指向同一个容器中的元素,或者是尾元素之后的位置。这两个元素通常是begin和end。
    这种元素范围被称为左闭合区间:[begin,end)

9.2.2 容器类型成员

  1. 主要有:size_type、iterator、const_iterator。

  2. 反向迭代器:第10章。

  3. 类型别名:value_type、reference、const_reference。
    使用示例:list<string>::iterator

9.2.3 begin和end成员

  1. begin()和end()成员由多个版本,以r开头的返回反向迭代器,以c开头的返回const迭代器。

  2. 与const指针和引用类似,可以将一个普通的iterator转换为const_iterator,反之不行。

  3. 当不需要写访问时,应使用cbegin和cend;

9.2.4 容器定义和初始化

  1. 除了array之外,其他容器都允许创建一个空容器。且都可以接受指定容器大小和元素初始值的参数。
    C c(b, e);:c初始化为迭代器b和e指定范围中的元素的拷贝(array不适用)

  2. 只有顺序容器(不包括array)的构造函数才能接受大小参数。
    C c(n);:c包含n个元素,默认初始化(string不适用)。
    C c(n, t);:c包含n个初始化为值t的元素。

  3. 一个容器初始化为另一个容器的拷贝时,两个容器的容器类型和元素类型必须相同。
    传递迭代器参数来来拷贝一个范围时,就不要求容器类型是相同的了。
    下面这个例子中,把const char*元素转换为string。

    vector<const char*> articles = { "a", "an", "the" };
    forward_list<string> words( articles.begin(), articles.end() );
    
  4. 列表初始化:除array以外。

  5. 只有顺序容器的构造函数才接受大小参数,关联容器并不支持。

  6. array必须指定固定大小:
    array<int, 42> myArray;

  7. 与内置数组的不同:内置数据不能进行拷贝、对象赋值操作,但array可以。

9.2.5 赋值和swap

  1. 对于array,使用初始值列表来:
    初始化时,赋值号右边可以缺省,以0填充。
    赋值时,赋值号右边不可以缺省(同类型同容量的array对象才能拷贝)

  2. 容器赋值运算:

    运算说明
    swap(c1,c2)交换元素,要求类型相同。swap通常比c2向c1拷贝元素快得多
    c1.swap(c2)同上
    c.assign(b, e)将c中元素替换为迭代器b和e之间的元素,且b和e不能指向c中元素
    c.assign(il)将c中的元素替换为初始化列表il中的元素
    c.assign(n,t)将c中的元素替换为n个初始值为t的元素
  3. assign操作仅适用于顺序容器,不适用于关联容器和array。
    assign操作允许从一个不同但相容的类型赋值,或从容器的一个子序列赋值。
    但,传递给assign的迭代器不能指向调用assign的容器

    list<string> names;
    vector<const char*> oldStyle;
    // 错误:容器类型不匹配
    names = oldStyles;
    // 正确:将vector中的一段char*赋予一个list中的string
    names.assign(oldStyle.cbegin(), oldStyle.cend());
    // 正确:10个元素,每个元素都是"Hi"
    list<string> myList(1);  // 1个元素
    myList.assign(10, "Hi");  // 10个元素
    
  4. 统一使用非成员版本的swap是一个好习惯。

  5. 除array外,swap函数只是交换了两个容器的内部数据结构。
    除array外,swap函数不对任何元素进行拷贝、删除、插入操作。

    vector<string> vec1(10);
    vector<string> vec2(24);
    swap(vec1, vec2);
    
  6. 除string外,使用swap函数,原容器的迭代器、引用、指针有效,但属于新的容器。
    对一个string调用swap,则原容器的迭代器、引用、指针失效。

  7. swap两个array会真正地交换它们的元素,所需时间与array中元素个数成正比。
    对于array,在swap操作后,指针、引用、迭代器所绑定的元素保持不变,但元素的值已经与另一个array中对应元素的值发生了交换。

9.2.6 容器大小操作

  1. empty()、max_size()、size()

  2. forward_list不支持size()操作。

9.2.7 关系运算符

  1. 除了无需关联容器外的所有容器都支持关系运算符(>、>=、<、<=)。

9.3 顺序容器操作

9.3.1 向顺序容器添加元素

  1. 向容器中添加元素的操作:

    • 这些操作会改变容器的大小,array不支持这些操作。

    • forward_list有自己的insert和emplace。

    • forward_list不支持push_back和emplace_back。

    • vector和string不支持push_front和emplace_front。

      操作说明
      c.push_back(t)在c的尾部创建一个值为t的元素,返回void
      c.emplace_back(args)在c的尾部创建一个由args创建的元素,返回void
      c.push_front(t)在c的头部创建一个值为t的元素,返回void
      c.emplace_front(args)在c的头部创建一个由args创建的元素,返回void
      c.insert(p,t)在迭代器p指向的元素之前创建一个值为t的元素,返回指向新添加的元素的迭代器
      c.emplace(p,args)在迭代器p指向的元素之前创建一个由args创建的元素,返回指向新添加的元素的迭代器
      c.insert(p,n,t)在迭代器p指向的元素之前插入n个值为t的元素,返回指向新添加的第一个元素的迭代器,若n为0则返回p
      c.insert(p,b,e)将迭代器b和e指定的范围内的元素插入到迭代器p指向的元素之前。b和e不能指向c中的元素。返回指向新添加的第一个元素的迭代器;若范围为空,则返回p
      c.insert(p,il)il是一个花括号包围的元素值列表。将这些给定值插入到迭代器p指向的元素之前。返回新添加的第一个元素的迭代器;若列表为空,则返回p
  2. 使用以上操作时,必须记得不同容器使用不同的策略来分配元素空间,而这些决策直接影响性能。
    在一个vector或string的尾部之外的任何位置,或是deque的首尾之外的任何位置添加元素,都需要移动元素。
    而且,向一个vector或string添加元素可能引起整个对象存储空间的重新分配。重新分配一个对象的存储空间需要重新分配新的内存,并将元素从旧的空间移动到新的空间当中。

  3. 容器元素是拷贝
    当我们用一个对象来初始化容器时,或将一个对象插入到容器时,实际上放入到容器中的是对象值的一个拷贝,而不是对象本身。
    就像我们将一个对象传递给非引用参数一样,容器中的元素与提供值的对象之间没有任何关联。

  4. 使用push_back:除了array、forward_list。

  5. 使用push_front:仅支持list、forward_list、deque。

  6. 使用insert操作:

    • 插入单个元素:将元素插入到vector、deque、string中的任何位置都是合法的,但这样做可能很耗时。
    • 插入范围元素:插入到指定位置之前。
  7. 使用insert的返回值:
    通过使用insert的返回值,可以在容器中一个特定位置反复插入元素。
    此例等价于调用push_front。

    list<string> lst;
    auto iter = lst.begin();
    while (cin >> word){
        iter = lst.insert(iter, word);
    }
    
  8. 使用emplace操作:直接在容器里构造。
    使用push、insert成员:传值,然后拷贝到容器里。

    c.emplace_back("12345", 25, 15.99);  // 使用三参数的构造函数
    c.push_back("12345", 25, 15.99);  // 错误:没有接受三参数的push_back
    c.push_back(Sales_data("12345", 25, 15.99));  // 正确:创建一个临时Sales_data对象传递给push_back
    
  9. emplace函数的参数根据元素类型而变化,参数必须与元素类型的构造函数向匹配。

    c.emplace_back();  // 使用默认构造函数
    c.emplace(iter, "12345");  // 使用Sales_data(string)
    c.emplace_front("12345", 25, 15.99);  // 使用三参数的构造函数
    

9.3.2 访问元素

  1. 包括array在内的每个顺序容器都有一个front函数。
    除了forward_list之外的所有顺序容器都有一个back函数。
    这两个操作分别返回首元素和尾元素的引用
    注:在调用front和back函数时,应确保容器非空。

  2. 访问成员函数(front、back、下标、at),返回的是引用。
    如果容器是一个const对象,则返回值是const的引用。

  3. 为了确保下标是合法的,可以使用at()成员函数。
    下标未越界时,等同于下标运算;
    越界时,at会抛出一个out_of_range异常

9.3.3 删除元素

  1. 非array容器也有很多种删除方式。
    但forward_list有特殊版本的erase,不支持pop_back。
    vector和string不支持pop_front。

    操作说明
    c.pop_back()删除c中尾元素。若c为空,则函数行为未定义。函数返回void
    c.pop_front()删除c中首元素。若c为空,则函数行为未定义。函数返回void
    c.erase§删除迭代器p所指定的元素,返回一个指向被删元素之后元素的迭代器。若p指向尾元素,则返回尾后迭代器。若p是尾后迭代器,则函数行为未定义
    c.erase(b,e)删除迭代器b和e所指定范围的元素。返回一个指向最后一个被删元素之后元素的迭代器,若e本身就是尾后迭代器,则函数也返回尾后迭代器
    c.clear()删除c中的所有元素,返回void
  2. 对于pop_front和pop_back,如果需要弹出的元素的值,应在执行弹出之前保留它。
    对于erase、clear也一样。

9.3.4 特殊的forward_list操作

  1. forward_list中添加或删除元素的操作是通过改变给定元素之后的元素来完成的。

  2. 由于以上原因,forward_list并未定义insert、emplace、erase,而是定义了名为insert_after、emplace_after、erase_after的操作。

  3. forward_list定义了一个before_begin,首前迭代器。

  4. 类似的,有以下操作:

    操作说明
    flst.before_begin()返回首前迭代器
    flst.cbefore_begin()返回首前const迭代器
    flst.insert_after(p,t)p后插入t,返回所插入元素的迭代器
    flst.insert_after(p,n,t)p后插入n个t,返回所插入的最后一个元素的迭代器
    flst.insert_after(p,b,e)p后插入b/e间元素,返回所插入的最后一个元素的迭代器
    flst.insert_after(p,il)p后插入花括号列表,返回所插入的最后一个元素的迭代器
    flst.emplace_after(p,args)p后用args创建元素,返回新元素的迭代器
    flst.erase_after§删除p后1个元素,返回被删元素之后元素的迭代器
    flst.erase_after(b,e)删除p后b/e间元素,返回被删元素之后元素的迭代器
  5. forward_list删除元素,较为复杂,示例代码如下:

    forward_list<int> flst = {0,1,2,3,4,5,6,7,8,9};
    auto prev = flst.before_begin();
    auto curr = flst.begin();
    while (curr != flst.end()) {
        if ( *curr % 2 ) {
            curr = flst.erase_after(prev);
        } else {
            prev = curr ++;
        }
    }
    

9.3.5 改变容器大小

  1. 可以用resize()来增大或缩小容器,array不支持resize操作。

    操作说明
    c.resize(n)调整c的大小为n个元素。多出的元素丢弃,不足数量则补充并值初始化
    c.resize(n,t)调整c的大小为n个元素,任何新添加的元素都初始化为值t
  2. 如果resize()缩小容器,则指向被删除元素的迭代器、引用、指针都会失效。

  3. 对vector、string、deque进行resize()可能导致迭代器、指针、引用失效。

9.3.6 容器操作可能使迭代器失效

  1. 向容器添加元素后:

    • 如果容器是vector或string,且存储空间被重新分配,则指向容器的迭代器、指针、引用都会失效。如果存储空间未重新分配,指向插入位置之前的元素的迭代器、指针、引用仍有效,但指向插入位置之后元素的迭代器、指针、引用将会失效。
    • 对于deque,插入到除首尾位置之外的任何位置都会导致迭代器、指针、引用失效。如果在首尾位置添加元素,迭代器会失效,但指向存在的元素的引用和指针不会失效。
    • 对于list和forward_list,指向容器的迭代器(包括尾后迭代器和首前迭代器)、指针、引用仍有效。
  2. 从一个容器中删除元素后:

    • 指向被删除的元素的迭代器、指针、引用会失效。
    • 对于list和forward_list,指向容器其他位置的迭代器(包括尾后迭代器和首前迭代器)、指针、引用仍有效。
    • 对于deque,如果再首尾之外的任何位置删除元素,那么指向被删除元素外其他元素的迭代器、指针、引用也会失效。如果是删除deque的尾元素,则尾后迭代器也会失效,但其他迭代器、引用、指针不受影响;如果是删除首元素,这些也不受影响。
    • 对于vector和string,指向被删除元素之前元素的迭代器、引用、指针仍有效。注意:当我们删除元素时,尾后迭代器总是会失效。
  3. 由于向迭代器添加元素和从迭代器删除元素的代码可能会使迭代器失效,因此必须保证每次改变容器的操作之后都正确地重新定位迭代器。

    • 对于vector、string、deque,如果循环中调用的是insert或erase,那么可以直接用返回值的结果作为迭代器的新值。
    • 对vector、string的元素添加/删除元素后,或在deque中首元素之外的任何位置添加/删除元素后,原来的end()返回的迭代器会失效。因此循环中我们应始终调用c.end()。

9.4 vector对象是如何增长的

  1. vector和string的实现通常会分配比新的空间需求各大的内存空间。
    此种做法提高了效率,其扩张操作比list和deque快。

  2. 管理容量的成员函数:

    • shrink_to_fit只适用于vector、string、deque。

    • capacity、reserve只适用于vector、string。

      操作说明
      c.shrink_to_fit()将capacity()减少为与size()相同大小
      c.capacity()不重新分配内存空间的话,c可以保存多少元素
      c.reserve()分配至少能容纳n个元素的内存空间
  3. reserve:只有当需要的内存空间超过当前容量时,reserve调用才会改变vector的容量。

    • 如果需求大小大于当前容量,reserve至少分配与需求一样大的内存空间(可能更大)。
    • 如果需求大小小于或等于当前容量,reserve什么都不做。
    • 特别地,当需求大小小于当前容量时,容器不会退回内存空间。因此,在调用reserve之后,capacity将会大于或等于传递给reserve的参数。
  4. 调用reserve永远不会减少容器内占用的内存空间。
    resize成员函数只改变容器中元素的数目,而不是容器的容量。
    同样,也不能用revise来减少容器预留的内存空间。

  5. shrink_to_fit可以要求vector、string、deque退回不需要的内存空间。但具体的实现可以忽略此请求。

  6. capacity和size区别:
    capacity:不分配新的内存空间的前提下最多可以保存多少元素。
    size:已保存的元素的数目。

  7. 每个vector的内存分配策略都不相同,但都一条原则:只有当迫不得已时才会分配新的内存空间。

  8. 确保用push_back向vector添加元素的操作有高效率。
    菜单技术角度来说,就是通过在一个初始为空的vector上调用n次push_back来创建一个n个元素的vector,所花费的时间不能超过n地常数倍。

9.5 额外的string操作

9.5.1 构造string的其他方法

  1. string类型支持的构造函数:

    string s;
    string s(s2);
    string s = s2;
    string s("value");
    string s = "value";
    string s(n, 'c');
    string s = {'a', 'b', 'c' };
    string s(b,e);  // b和e为另一string的首、尾后迭代器
    string s(cp, n);  // cp为字符串常量,s为cp前n个字符的拷贝
    string s(s2, pos2);  // s为s2从下标pos2开始的字符的拷贝
    string s(s2, pos2, len2);  // s为s2从下标pos2开始len2个字符的拷贝
    
  2. substr返回一个string,是原始string的一部分或全部的拷贝
    c.substr(pos, n):从下标pos开始n个字符的拷贝。

    string s("hello world");
    string s2 = s.substr(0, 5);   // s2 = hello
    string s3 = s.substr(6);      // s3 = world
    string s4 = s.substr(6, 11);  // s4 = world
    string s5 = s.substr(12);     // 抛出一个out_of_range异常
    

9.5.2 改变string的其他方法

  1. string支持assign、insert、erase操作。

    • insert、erase既支持迭代器,也支持下标。

    • insert、assign可接受C风格字符串。

      s.insert(s.size(), 5, '!'); // 在s末尾插入5个感叹号
      s.erase(s.size() - 5, 5);   // 从s删除最后5个元素
      const char *cp = "hello world";
      s.assign(cp, 5);            // s == "hello"
      s.insert(5, cp+5);          // s == "hello world"
      string s = "some string";
      string s2 = "some other string";
      s.insert(0, s2);            // s中0之前插入s2的拷贝
      s.insert(0, s2, 0, s2.size());  // s中0之前插入:s2中0到s2.size()
      
  2. append是末尾插入,replace是erase+insert的简写形式。
    其中,replace不要求插入的文本与删除的文本一样长。

    // append
    string s("C++ Primier"), s2 = s;
    s.insert(s.size(), " 4th Ed.");
    s2.append(" 4th Ed.");
    // replace
    s.erase(11, 3);
    s.insert(11, "5th");
    s2.replace(11, 3, "5th");
    
  3. 修改string的操作:

    操作说明
    s.insert(pos, args)在pos之前插入args指定的字符。pos可以是一个下标或迭代器。接受下标的版本返回一个指向s的引用;接受迭代器的版本返回一个指向第一个插入字符的迭代器。
    s.erase(pos, len)删除从位置pos开始的len个字符。如果len被省略,则删除从pos开始直至s末尾的所有字符。返回一个指向s的引用。
    s.assign(args)将s中的字符替换为args指定的字符。返回一个指向s的引用
    s.append(args)将args追加到s。返回一个指向s的引用
    s.replace(range, args)删除s中范围range内的字符,替换为args指定的字符。range可以是一个下标、长度、指向s的迭代器。返回一个指向s的引用。
    • 当调用append、assign时,args可以是下列形式之一:

      • str
      • str, pos, len
      • cp, len
      • cp
      • n, c
      • b, e
      • 初始化列表
    • 当调用replace、insert时,args形式依赖于range和pos:

      argsreplacereplaceinsertinsert
      -pos,len,argsb,e,argspos,argsiter,args
      str
      str,pos,len
      cp,len
      cp
      n,c
      b2,e2
      初始化列表

9.5.3 string搜索操作

  1. string类提供了6个不同的搜索函数,每个函数都有4个重载版本。
    它们都返回string::size_type值,表示匹配发生位置的下标。
    若匹配失败,则返回一个string::npos的static成员。
    标准库将npos定义为一个const string::size_type类型,并初始化为-1。
    由于npos是一个unsigned类型,此初始值意味着npos等于任何string最大的可能大小。

  2. 不建议使用int、或其他带符号类型的变量来保存函数的返回值。
    搜索操作返回指定字符出现的下标,如果未找到则返回npos。
    这些函数都对大小写敏感。

    操作说明
    s.find(args)查找s中args第一次出现的位置
    s.rfing(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中的字符
  3. args必须是以下形式之一:

    形式说明
    c, pos从s中位置pos开始查找字符c。pos默认为0
    s2, pos从s中位置pos开始查找字符串s2。pos默认为0
    cp, pos从s中位置pos开始查找指针cp指向的以空字符结尾的C风格字符串。pos默认为0
    cp, pos, n从s中位置pos开始查找指针cp指向的数组的前n个字符。pos和n无默认值

9.5.4 compare函数

  1. 根据s是否等于、大于、小于参数指定的字符串,s.compare返回0、正数、负数。
    s.compare有6个版本:

    参数形式说明
    s2比较s和s2
    pos1, n1, s2, pos2, n2将s中从pos1开始的n1个字符与s2中pos2开始的n2个字符进行比较
    cp比较s与cp指向的以空字符结束的字符数组(C风格字符串)
    pos1, n1, cp将s中从pos1开始的n1个字符与cp指向的以空字符结束的字符数组(C风格字符串)进行比较
    pos1, n1, cp, n2将s中从pos1开始的n1个字符与指针cp指向的地址开始的n2个字符进行比较

9.5.5 数值转换

  1. 数值数据与标准库string之间的转换:

    • string参数中第一个非空字符必须是符号(+或-)或数字。它可以以0x或0X开头来表示十六进制数。

    • 对于那些将字符串转换为浮点值的函数,string参数也可以以小数点(.)开头,并可以包含e或E来表示指数部分。

    • 对于那些将字符串转换成整型值的函数,根据基数不同,string参数可以包含字母字符,对应大于9的数。

    • 如果string不能转换成一个数值,这些函数抛出一个invalid_argument异常。如果转换得到的数值无法用任何类型表示,则抛出一个out_of_range异常。

      int i = 42;
      string s = too_string(i);
      double d = stod(s);
      string s2 = "pi = 3.14";
      d = stod(s2.substr(s2.find_first_of("+-.0123456789")));
      
  2. string和数值之间的转换

    函数说明
    to_string(val)一组重载函数,返回数值val的string表示。val可以是任何算术类型。对每个浮点类型和int或更大的整型,都有相应版本的to_string。与往常一样,小整型会被提升。
    stoi(s,p,b), stol(s,p,b), stoul(s,p,b), stoll(s,p,b), stoull(s,p,b)返回s的起始子串(表示整数内容)的数值,返回值类型分别是int、long、unsigned long、long long、unsigned long long。b表示转换所用的基数,默认值为10。p是size_t指针,用来保存s中第一个非数值字符的下标,p默认为0,即函数默认不保存下标
    stof(s,p), stod(s,p), stold(s,p)返回s的起始字串(表示浮点数内容)的数值,返回类型分别是float、double、long double。参数p的作用与整数转换函数中一样。

9.6 容器适配器

  1. 所有容器适配器都支持的操作和类型

    操作说明
    size_type一种类型,足以保存当前类型的最大对象的大小
    value_type元素类型
    container_type实现适配器的底层容器类型
    A a;创建一个名为a的空适配器
    A a©;创建一个名为a的适配器,带有容器c的一个拷贝
    关系运算符每个适配器都支持所有关系运算符:==、!=、<、<=、>、>=。这些运算符返回底层容器的比较结果
    a.empty()若a包含任何元素,则返回false,否则返回true
    a.size()返回a中的元素数目
    swap(a,b), a.swap(b)交换a和b的内容,a和b必须要有相同的类型,包括底层容器类型也必须相同
  2. 定义一个适配器:
    默认情况下,stack和queue是基于deque实现的,priority_queue是在vector上实现的。
    我们可以在创建一个适配器时将一个命名的顺组容器作为第二个类型参数,来重载默认容器类型。

    deque<int> deq;
    stack<int> stk(deq);
    stack<string, vector<string>> str_stk;
    stack<string, vector<string>> str_stk2(svec);
    
  3. 对于一个给定的适配器,可以使用的容器种类是有限制的,所有适配器必须具有添加和删除元素的能力。
    因此,容器不能构造在array之上。
    类似的,也不能用forward_list来构造适配器,因为所有适配器都要求容器具有添加、删除以及访问尾元素的能力。

  4. stack要求push_back、pop_back、back操作,因此可以使用除array、forward_list之外的任何容器。
    queue要求back、push_back、front、push_front操作,因此可以构造于list、qeque之上,不能基于vector。
    priority_queue要求front、push_back、pop_back、随机访问的操作,因此可以构造于vector、deque之上,不能基于list。

  5. 栈适配器:定义于stack头文件中。
    默认基于deque实现,也可以基于list、vector实现。

    操作说明
    s.pop()删除栈顶元素,但不返回该元素值
    s.push(item)创建一个新元素压入栈顶,该元素通过拷贝或移动item而来
    s.emplace(args)创建一个新元素压入栈顶,该元素由args构造
    s.top()返回栈顶元素,但不将元素弹出栈
  6. 队列适配器:
    queue和priority_queue,都定义于queue头文件中。
    queue默认基于deque实现,也可以用list、vector实现。
    priority_queue默认基于vector实现,也可以用deque实现。

    操作说明
    q.pop()返回queue的首元素或priority_queue的最高优先级的元素,但不删除此元素
    q.front()(只适用于queue)返回首元素,但不删除此元素
    q.back()(只适用于queue)返回尾元素,但不删除此元素
    q.top()(只适用于priority_queue)返回最高优先级元素,但不删除此元素
    q.push(item)在queue末尾或priority_queue中恰当的位置创建一个元素,其值为item
    q.emplace(args)在queue末尾或priority_queue中恰当的位置创建一个元素,其值由args创造
  7. 标准库queue使用一种先进先出(FIFO)的存储和访问策略,进入队列的对象被放置到队尾,而离开队列的对象则从队首删除。

  8. priority_queue允许我们为队列中的元素建立优先级。新加入的元素会排在所有优先级比它低的已有元素之前。
    默认情况下,标准库在元素类型上使用<运算符来确定相对优先级。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值