点击查看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

###学习《C++ Primer》- 2的更多相关文章

  1. 学习C++ Primer 的个人理解(一)

    <C++ Primer>这本书可以说是公认的学习C++最好的书,但我觉得不是特别适合作为教材,书中内容的顺序让人有些蛋疼.我个人认为初学此书是不能跳着看的.如果急于上手的话,我更推荐< ...

  2. 学习C++ Primer 的个人理解(九)

    这一章介绍顺序容器,在之前的第三章中,了解到的vector就属于顺序容器的一种. 一个容器就是一些特定类型对象的集合. 除了vector,还有哪些顺序容器? vector: 大小可变,随机访问的速度很 ...

  3. 学习C++ Primer 的个人理解(三)

    第三章,主要内容是字符串和数组.感觉作者的意图是希望读者可以早一点可以写出简单的小程序,并且可以早点接触迭代器这种思想. 在我看来,这种内容的难度并不大. 对于编程来说,最重要的应该是思想,类似vec ...

  4. 学习C++ Primer 的个人理解(二)

    本身就一定基础的读者我想变量常量这些概念应该已经不是问题了.但是本章还是有几个重点,需要特别留意一下的: 1.初始化和赋值是不同的操作 2.任何非0值都是true 3.使用新标准列表初始化,在有丢失精 ...

  5. 学习C++.Primer.Plus 11 使用类

    1.操作符重载 重载操作符的几个限制: a)         重载的至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载操作符. b)         不能违反操作符原有来的句法规则. c)  ...

  6. 学习C++.Primer.Plus 10 对象和类

    1.类的声明和定义 类的声明和定义. 类声明的格式如下: class className { private://private 是类对象的默认访问控制,因此,可以省略 data member del ...

  7. 学习C++.Primer.Plus 8 函数探幽

    1. 内联函数 普通函数调用: 存储调用指令的地址->将函数参数复制到堆栈->跳到函数地址执行代码(返回值放到寄存器)->跳回调用指令处 2.  当代码执行时间很短,且会被大量调用的 ...

  8. 学习C++.Primer.Plus 7 函数

    C++的返回值类型不能是数组 函数原型中的变量名相当于点位符,因此不要求提供变量名. void cheers(int); C++中不指定参数列表时就使用活力号: void saybye(...); 通 ...

  9. 学习C++.Primer.Plus 6 分支语句和逻辑操作符

    ||. &&操作符是一个顺序点 < 操作符从左向右结合 ; < age < )//17<age为true, = 1,肯定 < 27.所以为整个条件为tru ...

  10. 学习C++.Primer.Plus 5 循环和关系表达式

    C++将赋值表达式的值定义为左侧成员的值 赋值操作符是自右向左结合的 cout.setf(ios:: boolalpha);//调用设置标记,命令cout输出true或false,而非1或0. 任何表 ...

随机推荐

  1. hdoj 2023 求平均成绩

    求平均成绩 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...

  2. hdoj 2097 Sky数

    Sky数 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  3. poj 4045 (树形DP)

    先选一点为根节点找出所有父节点i到下面所有点距离和dp[i],该父节点下面有多少个点Node[i]. 然后求出所有节点的所有非子节点到该点的距离dp1[v]+=(dp1[u]+(dp[u]-dp[v] ...

  4. Delphi- 一些H8记录

    CheckOrder方法写在uDataModConn类里.

  5. JavaScript要点 (四)JSON

    JSON 是用于存储和传输数据的格式. JSON 通常用于服务端向网页传递数据 . 什么是 JSON? JSON 英文全称 JavaScript Object Notation JSON 是一种轻量级 ...

  6. oracle查询前10条记录

    select * from table_name where rownum<11;

  7. 高级Bash脚本编程指南

    http://www.cnblogs.com/rollenholt/archive/2012/04/20/2458763.html

  8. Linux性能优化--CPU[备忘]

    http://kumu-linux.github.io/blog/2014/04/21/performance-cpu/

  9. [Effective C++ --022]将成员变量声明为private

    这一章并没有什么太多的内容,作者无非想告诉我们一件事:成员变量应该是private. 为此,他列举了以下的理由: 1.成员函数来返回成员变量是非常高效: 2.protected成员变量并不比publi ...

  10. 判断ie,并确定其版本号

    var UA = navigator.userAgent,isIE = UA.indexOf('MSIE') > -1,v = isIE ? /\d+/.exec(UA.split(';')[1 ...