###学习《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函数,vector和string没有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进行字符串处理,可以极大地简化我们的工作。
一般程序设计语言,都会提供
substr函数进行字符串截取。//pos开始位置,n截取的个数
s.substr(pos, n);
insert,erase基于下标版本
string提供了基于下标版本的成员函数。s.insert(s.size(), 5, '!'); //在最后插入5个!
s.erase(s.size()-5, 5); //删除最后5个元素
append,replacestring s("C++ Primer"), s2 = s;
s.insert(s.size(), " 4th Ed."); //追加
s.append(" 4th Ed."); //等价上面insert方法
replace是erase和insert操作的简写:
s.erase(11, 3);
s.insert(11, "5th");
s2.replace(11, 3, "5th"); //等价于上面两句
s2.replace(11, 3, "Fifth"); //删除3个字符,添加5个也是可以的搜索操作
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第一次出现的地方
数值转换
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的更多相关文章
- 学习C++ Primer 的个人理解(一)
<C++ Primer>这本书可以说是公认的学习C++最好的书,但我觉得不是特别适合作为教材,书中内容的顺序让人有些蛋疼.我个人认为初学此书是不能跳着看的.如果急于上手的话,我更推荐< ...
- 学习C++ Primer 的个人理解(九)
这一章介绍顺序容器,在之前的第三章中,了解到的vector就属于顺序容器的一种. 一个容器就是一些特定类型对象的集合. 除了vector,还有哪些顺序容器? vector: 大小可变,随机访问的速度很 ...
- 学习C++ Primer 的个人理解(三)
第三章,主要内容是字符串和数组.感觉作者的意图是希望读者可以早一点可以写出简单的小程序,并且可以早点接触迭代器这种思想. 在我看来,这种内容的难度并不大. 对于编程来说,最重要的应该是思想,类似vec ...
- 学习C++ Primer 的个人理解(二)
本身就一定基础的读者我想变量常量这些概念应该已经不是问题了.但是本章还是有几个重点,需要特别留意一下的: 1.初始化和赋值是不同的操作 2.任何非0值都是true 3.使用新标准列表初始化,在有丢失精 ...
- 学习C++.Primer.Plus 11 使用类
1.操作符重载 重载操作符的几个限制: a) 重载的至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载操作符. b) 不能违反操作符原有来的句法规则. c) ...
- 学习C++.Primer.Plus 10 对象和类
1.类的声明和定义 类的声明和定义. 类声明的格式如下: class className { private://private 是类对象的默认访问控制,因此,可以省略 data member del ...
- 学习C++.Primer.Plus 8 函数探幽
1. 内联函数 普通函数调用: 存储调用指令的地址->将函数参数复制到堆栈->跳到函数地址执行代码(返回值放到寄存器)->跳回调用指令处 2. 当代码执行时间很短,且会被大量调用的 ...
- 学习C++.Primer.Plus 7 函数
C++的返回值类型不能是数组 函数原型中的变量名相当于点位符,因此不要求提供变量名. void cheers(int); C++中不指定参数列表时就使用活力号: void saybye(...); 通 ...
- 学习C++.Primer.Plus 6 分支语句和逻辑操作符
||. &&操作符是一个顺序点 < 操作符从左向右结合 ; < age < )//17<age为true, = 1,肯定 < 27.所以为整个条件为tru ...
- 学习C++.Primer.Plus 5 循环和关系表达式
C++将赋值表达式的值定义为左侧成员的值 赋值操作符是自右向左结合的 cout.setf(ios:: boolalpha);//调用设置标记,命令cout输出true或false,而非1或0. 任何表 ...
随机推荐
- Idea的Git使用
在使用Git来pull和push代码的时候会发生冲突的情况,如果处理不当则会出现无法pull和push代码的情况.所以这个是十分有必要注意的. 那么我来说几句自己的总结吧: 1.先看下idea的Git ...
- sudo: /etc/sudoers is mode 0640, should be 0440解决办法
ubuntu或者CentOS中,/etc/sudoer 的权限为 0440时才能正常使用,否则sudo命令就不能正常使用.出现类似:sudo: /etc/sudoers is mode 0640, s ...
- iOS 强制横屏
// // AAAAViewController.m // hengp // // Created by 朱信磊 on 15/2/13. // Copyright (c) 2015年 niit. Al ...
- android常见错误-The container 'Android Dependencies' references non existing library
The container 'Android Dependencies' references non existing library
- node.js在windows下的学习笔记(8)---进程管理Process
process是一个全局内置对象,可以在代码中的任何位置访问此对象,这个对象代表我们的node.js代码宿主的操作系统进程对象. 使用process对象可以截获进程的异常.退出等事件,也可以获取进程的 ...
- ORACLE 毫秒与日期的相互转换
毫秒转换为日期 SELECT TO_CHAR(1406538765000 / (1000 * 60 * 60 * 24) + TO_DATE('1970-01-01 08:00:00', 'YYYY- ...
- sessionID和cookie
一.cookie机制和session机制的区别***************************************************************************** ...
- 初学Node.js第一天
最近开始下班到家不知道该做啥,因为水平太菜,要学的东西实在太多,反而陷入了不知道该学什么的困境,结果天天就是看别人的博客,看到什么标题比较感兴趣就点进去,没有一个目标. 今天突然兴起,决定要捣鼓捣鼓N ...
- Cookie API
Cookie API All cookies created by the Nova framework are encrypted and signed with an authentication ...
- Asp.Net 之 获取GridView行的DataKeys
1.后台任意一个位置获取GridView行的主键值 foreach (GridViewRow row in GridView1.Rows) { string order_Id=this.GridVie ...