zoukankan      html  css  js  c++  java
  • ###学习《C++ Primer》- 2

    点击查看Evernote原文

    #@author:       gr
    #@date:         2014-10-01
    #@email:        forgerui@gmail.com
    

    Part 2: STL顺序容器(第9章)

    一、标准库array(C++11)

    array的大小固定,不能省去,它是容器的一部分。

    //保存42个int, 初始1,2,其余40个元素初始为0
    array<int, 42> = {1, 2}
    //保存24个string
    array<string, 24>
    array<int, 10>::size_type i;  //正确
    array<int>::size_type j;      //错误,没有大小
    //类的话必须有一个默认构造函数
    array<Widget, 10> aw;
    

    内置数组不能进行数组赋值,拷贝,array可以。

    int digs[10] = {1, 2, 3};
    int cpy[10] = digs;         //错误,不支持赋值
    array<int, 10> dig = {1, 2, 3};
    array<int, 10> cp = dig;    //正确,可以进行赋值,数组大小要一致
    

    二、容器的关系运算符

    只有当元素定义了相应的比较运算符,才可以使用容器的关系运算符。

    vector<int> v1 = {1, 3, 7};             //初始列,C++11
    vector<int> v2 = {1, 3, 7, 9};
    v1 < v2         //true,所有元素相等,v1数目更少
    

    三、STL容器使用的是拷贝

    用一个对象初始化容器,是将该对象的拷贝放进容器中的,容器中的元素修改与原来的对象没有任何影响。

    四、使用emplace_back,emplace,emplace_front

    emplace可以直接在内存中创建对象,并且效率更高。

    vector<Sales_data> c;
    //直接在空间创建对象,节省开销
    c.emplace_back("123", 25, 25.99);
    //编译错误,没有3个参数的构造
    c.push_back("123", 25, 25.99);
    //构造临时对象,拷贝进入容器,销毁对象
    c.push_back(Sales_data("123", 25, 25.99));
    

    五、容器的访问成员函数

    可以使用front,back进行直接访问,也可以通过迭代器进行解引用,获取元素,但在进行访问之前,最好要检测容器是否为空,如果为空,将会产生不确定行为。

    对提供了随机访问的容器可以使用下标访问(如v[0]),list则不存在下标访问。下标访问返回的是元素的引用,对其赋值可以直接修改容器中元素的值。下标运算不检查是否在合法范围内,下标越界是一种严重的错误,会导致runtime error。也可以使用at成员函数,如果下标越界,at会抛出一个out_of_range异常。

    vector<string> sv;      //空vector
    cout << sv[0];          //运行时错误
    cout << sv.at(0);       //抛出一个out_of_range
    

    六、容器的删除操作

    forward_list没有pop_back函数,vectorstring没有pop_front操作。erase(p);删除迭代器p所指定的元素,erase(p, q);删除迭代器p到q范围的元素。在删除之前,必须保证元素是存在的。
    forward_list的删除需要两个迭代器,一个是正在处理元素的,一个是前驱元素的迭代器。

    //删除容器中的奇数
    forward_list<int> flst = {0, 1, 2, 3, 4, 5, 6, 7};
    auto prev = flst.before_begin();                //表示flst的“首前元素”
    auto curr = flst.begin();
    while (curr != flst.end()) {
        if (*curr % 2){                             //元素为奇数
            curr = flst.erase_after(prev);          //删除它并移动curr
        }else{
            prev = curr;
            curr++;
        }
    }
    

    七、改变容器的大小

    使用resize来改变容器的大小,array不支持resize。如果当前大小大于所要求的大小,容器后部的元素会被删除;如果当前大小小于要求的大小,将新元素加入到容器中。

    list<int> il(10, 42);
    il.resize(15);          //在最后加入5个0
    il.resize(25, -1);      //在最后加入10个-1
    il.resize(5);           //删除末尾的20个元素
    

    八、在循环中保持迭代器

    容器的操作可能会使已有的迭代器失效,如何在循环过程中保持这些迭代器,以使整个循环过程正确?
    办法是使用成员函数操作的返回值更新迭代器,虽然list的某些操作并不影响迭代器,但为了统一,最好所有的顺序容器都使用相同的形式。

    vector<int> vi = {1, 2, 3, 4, 5};
    auto iter = vi.begin();
    iter = vi.insert(iter, 2);      //用insert返回值更新迭代器
    iter = vi.erase(iter);          //删除元素
    

    九、不要试图保存end返回的迭代器

    容器的操作会改变end()返回的迭代器,所以不要保存它。这也是为什么在循环中,为何反复调用v.end()进行判断是否结束。

    it = v.begin();
    while (it != v.end()){      //反复调用v.end()检查
        //...
    }
    

    十、理解vector是如何增长的

    capacity表示容量,是已经申请的内存空间,size是已经保存的元素的数目。
    使用reserve改变容器的容量,resize改变容器的大小。

    vector内存不够用的时候,会把整个vector复制到更大的内存空间中,造成开销变大。
    使用reserve事先申请大的空间,可以避免vector的拷贝,但如果容量过大,则会浪费。
    《Effective STL》中的Topic17里面讲解使用"swap技巧"去除多余的容量,实际上,C++11中提供了shrink_to_fit函数来退回不需要的内存空间。

    十一、string独有的一些操作

    使用string进行字符串处理,可以极大地简化我们的工作。

    1. 一般程序设计语言,都会提供substr函数进行字符串截取。

       //pos开始位置,n截取的个数
       s.substr(pos, n);
      
    2. insert, erase基于下标版本
      string提供了基于下标版本的成员函数。

       s.insert(s.size(), 5, '!');     //在最后插入5个!
       s.erase(s.size()-5, 5);         //删除最后5个元素
      
    3. append,replace

       string s("C++ Primer"), s2 = s;
       s.insert(s.size(), " 4th Ed.");     //追加
       s.append(" 4th Ed.");               //等价上面insert方法
      

      replaceeraseinsert操作的简写:
      s.erase(11, 3);
      s.insert(11, "5th");
      s2.replace(11, 3, "5th"); //等价于上面两句
      s2.replace(11, 3, "Fifth"); //删除3个字符,添加5个也是可以的

    4. 搜索操作
      string一共有6种搜索操作,分别是find, rfind, find_first_of, find_first_not_of, find_last, find_last_not_of。这些都是大小写敏感,返回第一个匹配出现的位置。

       string name("AnnaBelle");
       name.find("Anna");          //返回0
       //使用find_first_of查找在list_c字符出现的第一个位置
       string list_c("cnl");
       name.find_first_of(list_c); //返回1,即n第一次出现的地方
      
    5. 数值转换

       int i = 42;
       string s = to_string(i);    //将i转换为字符表示形式
       double d = stod(s);         //将string转换为浮点数
      

    十二、容器适配器

    适配器是一种机器,接受一种已有的容器类型,对其改造,使其行为看起来像一种不同的类型。我们定义了适配器,就只能使用适配器提供的操作,不能使用底层容器的操作。
    stack栈适配器可以使用vector,deque,list实现;queue可以使用deque,list实现,因为vector没有pop_front

  • 相关阅读:
    最长公共子序列
    小测试 炒书
    洛谷P1968 美元汇率
    洛谷P3611 [USACO17JAN]Cow Dance Show奶牛舞蹈
    【刷题】【树】【模拟】树网的核
    【刷题】【dp】地精的贸易
    【刷题】【模拟】复制cs
    【刷题】【模拟】3n+1
    【刷题】【dp】中国象棋
    【刷题】【搜索】新数独
  • 原文地址:https://www.cnblogs.com/gr-nick/p/4069782.html
Copyright © 2011-2022 走看看