您的位置:首页 > 编程语言 > C语言/C++

C++ primer 读书笔记 第九章 顺序容器

2016-04-26 21:27 295 查看
1、顺序容器:

vector:支持快速随机访问

deque:双端队列

list:支持快速插入/删除

2、顺序容器适配器:

stack:后进先出(LIFO)栈

queue:先进先出(FIFO)队列

priority_queue:有优先级队列管理的队列

3、所有的容器都是类模板。定义一个容器类型的对象之前必须包含相关头文件

#include <vector>

#include <list>

#include <deque>

4、容器元素类型的两个约束条件:

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

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

除输入输出(IO)标准库类型之外,所有其他标准库类型都是有效的容器元素类型。特殊地,容器本身也满足上述要求。IO库类型不支持复制或赋值,所以不能创建存放IO类型对象的容器。

在容器本身作为容器的元素时,必须使用如下空格:

vector<vector<int> > ivec;

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

5、迭代器范围是一个左闭合区间,其标准表示方式为:[ first, last)。

first 和last迭代器必须满足下面两个条件:

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

2) last绝对不能位于first之前。

6、使迭代器失效的容器操作

一些容器操作会修改容器内在状态或移动容器内的元素,这样的操作会使所有指向元素的迭代器失效,同时也使其它迭代器失效。使用失效的迭代器是没有定义的,可能会导致与悬垂指针相同的问题。

1) 容器的erase函数

c.erase(p) 删除迭代器p所指向的元素

返回一个迭代器,它指向被删除元素后面的元素。如果p指向容器中得最后一个元素,则返回的迭代器指向容器的超出末端的下一个位置。如果p就是指向超出容器末端的下一个位置的迭代器,则该操作未定义。

for (vector<int>::iterator iter = ivec.begin(); iter != ivec.end();++iter) {
if (*iter % 2 != 0) {
iter = ivec.erase(iter);
<strong>--iter</strong>;
}
}


erase删除元素后返回指向下一个元素的迭代器,而for循环中迭代器自增,所以if语句中要将迭代器减去1,以免漏处理元素。

2)任何insert 或 push 操作(添加元素操作)都可能导致迭代器失效。当编写循环将元素插入到vector或者deque容器中时,程序必须确保迭代器在每次循环后都得到更新。

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

while(first != v.end()){
first = v.insert(first, 42);
<strong>++first;</strong>
}


3)不要存储end操作返回的迭代器。可以在每次做完插入运算后重新计算end迭代器值。

for(vector<int>::iterator iter = ivec.begin(); <strong>iter != ivec.end()</strong>; ++iter){
*iter = 0;
}


7、容器元素都是副本

在容器中添加元素时,系统是将元素值复制到容器里。容器里的元素与原来的元素互不相干。

8、vector容器的自增长

为了支持快速的随机访问,vector容器的元素以连续的方式存放。因为元素是连续存放的,如果在添加一个元素的时候,容器已经没有足够的空间容纳新的元素,vector为保证连续存放元素,必须重新分配存储空间,用于存放原来的元素和新添加的元素,最后撤销就得空间。如果vector容器在每次添加新元素时,都要这么分配和撤销内存空间,其性能将会非常缓慢。

对于不连续存储元素的容器,不存在这样的内存分配问题。在list容器添加一个新元素非常方便。

由此可以推论,一般而言,使用list容器优于vector容器。但事实上,对于大部分应用,使用vector容器是最好的。原因在于,标准库的实现者使用这样的内存分配策略:以最小的代价连续存储元素。由此而带来的访问元素的遍历弥补了其存储代价。

为了使vector容器实现快速的内存分配策略,其实际分配的容量要比当前所需的空间多一些。vector预留了额外的存储区,用于存放新添加的元素。

win7 + eclipse + 32位系统下测试:

vector<int> ivec;
cout << "size:"<<ivec.size()<<"\t";
cout << "capacity:"<<ivec.capacity()<<endl;
for (vector<int>::size_type ix = 0; ix !=24;++ix) {
ivec.push_back(ix);
cout << "size:"<<ivec.size()<<"\t";
cout << "capacity:"<<ivec.capacity()<<endl;
}


输出结果如下:

size:0	capacity:0
size:1	capacity:1
size:2	capacity:2
size:3	capacity:4
size:4	capacity:4
size:5	capacity:8
size:6	capacity:8
size:7	capacity:8
size:8	capacity:8
size:9	capacity:16
size:10	capacity:16
size:11	capacity:16
size:12	capacity:16
size:13	capacity:16
size:14	capacity:16
size:15	capacity:16
size:16	capacity:16
size:17	capacity:32
size:18	capacity:32
size:19	capacity:32
size:20	capacity:32
size:21	capacity:32
size:22	capacity:32
size:23	capacity:32
size:24	capacity:32


可以看出这个编译器中的分配策略是:当内存空间不够的时候加倍内存空间。
capacity:0 -> 2^0 -> 2^1 -> 2^2 -> 2^3 -> 2^4 -> 2^5 ->......

9、容器的选用

vector和的确容器提供了对元素的快速随机访问,但代价是,在容器的任意位置插入或删除元素,比在容器尾部插入和删除的开销更大。list类型在任何位置都能快速插入和删除,但付出的代价是元素的随机访问开销更较大。

1)插入操作影响容器的选择:

list容器表示不连续的内存区域,在任何位置都能搞笑地inser或erase一个元素。插入或删除一个元素不需要移动其他任何元素。

对于vector容器,除了尾部外,在任何其他位置的插入和删除操作都要求移动被插入或者删除元素右边的所有元素。

deque容器拥有更复杂的数据结构。在deque容器的中间insert或erase元素效率较低,但是在deque首部和尾部能快速实现insert和erase操作。

2)元素的随机访问影响容器的选择:

vector和deque容器都支持对元素实现高效的随机访问。在list容器中要实现随机访问只能是顺序跟随指针,效率很低。

3)选择容器类型的法则:

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

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

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

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

(5)如果程序既要随机访问又必须在容器中间位置插入或删除元素,此时选择容器取决于下面两种操作付出的代价:随机访问list容器元素的代价,在vector或deque容器中插入或删除元素时复制元素的代价。容器的选择主要取决于程序中使用更多的是访问操作还是插入或删除操作。

(6)如果无法确定改采用哪种容器,则编写代码时尝试只使用vector和list容器都提供的操作,使用迭代器,而不是下标,并且避免随机访问元素。这样在必要时,可以很方便的将程序从使用vector容器修改为list容器。

注:通常来说,除非找到选择使用其他容器的更好的理由,否则vector容器都是最佳选择。

10、string类型

1)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的字符

eg:查找字符串中第一个出现的数字

string number = "0123456789";
string str = "r2d2";
string::size_type pos = str.find_first_of(number);
cout << "found number at index: " << pos <<
"element is: " << str[pos] << endl;
2)string对象的比较函数

s.compare(s2) 比较s和s2,返回值可能是正数(s > s2)、0(s == s2)、负数(s < s2)。

11、容器适配器

这块还不是很理解其作用是什么。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: