首页 > 代码库 > C++ Primer笔记5_STL之顺序容器
C++ Primer笔记5_STL之顺序容器
1.STL(Standard Template Library)
标准模板库。从根本上说,STL是一些“容器”的集合,这些“容器”有list, vector,set,map等,STL也是算法和其它一些组件的集合。这里的“容器”和算法的集合指的是世界上很多聪明人很多年的杰作。每一个C++程序员都应该好好学习STL。大体上包括container(容器)、algorithm(算法)和iterator(迭代器),容器和算法通过迭代器可以进行无缝连接。
2.顺序容器:
2.1顺序容器类型:
vector 可变大小数组。支持快速随机访问。在尾部之外的位置插入/删除元素速度可能很慢。
deque 双端队列。支持快速随机访问。在头尾插入/删除元素速度很快。
list 双向链表。只支持双向顺序访问。在list任何位置插入/删除速度都很快。
string 与vector类似。
*forward_list 单向链表。只支持单向顺序访问。在list任何位置插入/删除速度都很快。
*array 固定大小数组。array与forward_list是C++新标准增加的类型
如果程序要求随机访问元素,应使用vector或deque;
如果要求程序能快速的在中间插入和删除,应使用list或forward_list;
如果程序要求在头尾位置插入或删除元素,但不会在中间插入或删除,使用deque;
2.2初始化方式:
以vector为例:
vector<int> v; vector<int> v1(v); vector<int> v(10);//10个元素,都为0 vector<int> v(10, 1);//10个元素,都为1 vector<int> v{1, 2, 3};// C++11中支持 vector<int> v = {1, 2, 3};// C++11中支持
#include <iostream> #include <vector> using namespace std; int main() { string s; vector<string> vecStr; while(cin >> s) { vecStr.push_back(s); } for(vector<string>::const_iterator i = vecStr.begin(); i!=vecStr.end(); i++) { cout << *i << endl; } return 0; }
list<deque<int> > l;
注意:
1)所有的容器基本都支持insert(插入)、erase(删除)、clear(清除)操作。
2)当用一个对象初始化容器时,或将一个对象插入到容器时,实际放入容器中的是一个值的拷贝。不是对象本身。
就像将一个对象传递给非引用的函数参数一样。随后对容器中的对象做任意改变都不会影响到原始对象。
2.3顺序容器操作:
添加元素:
c.pusk_back(t) 在c尾部加入元素t——list、deque、vector支持
c.push_front(t) 在c头部加入元素t ——list、deque支持,vector不支持。
c.insert(p, t) 在迭代器p指向的元素之前创建一个值为t的元素,返回指向新元素的迭代器。
c.insert(p, n, t) 在迭代器p指向的元素之前插入n个值为t的元素,返回指向新添加第一个元素的迭代器。
c.insert(p, b, e)将迭代器b和e指定的范围内的元素插入到迭代器p指向的元素之前。
使用deque中的push_front:
#include <iostream> #include <vector> #include <deque> #include <list> using namespace std; int main() { string s; deque<string> vecStr; while(cin >> s) { vecStr.push_front(s); } for(deque<string>::const_iterator i = vecStr.begin(); i!=vecStr.end(); i++) { cout << *i << endl; } return 0; }
使用insert1:
#include <iostream> #include <vector> #include <deque> #include <list> using namespace std; int main() { string s; deque<string> vecStr; deque<string>::iterator idx = vecStr.begin(); vecStr.insert(idx, "Hello");//insert ibefore first element //same as vecStr.push_front("Hello"); while(cin >> s) { vecStr.push_back(s); } for(deque<string>::const_iterator i = vecStr.begin(); i!=vecStr.end(); i++) { cout << *i << endl; } return 0; }
使用insert2:
#include <iostream> #include <vector> #include <deque> #include <list> using namespace std; int main() { string s; deque<string> vecStr; deque<string>::iterator idx = vecStr.begin(); vecStr.insert(idx, "Hello");//insert ibefore first element //same as vecStr.push_front("Hello"); while(cin >> s) { vecStr.push_back(s); } string sArr[] = {"I", "Love", "You"}; deque<string> vec(sArr, sArr+sizeof(sArr)/sizeof(sArr[0])); //deque<string> vec(3,"You"); vecStr.insert(idx, vec.begin(), vec.end()); for(idx = vecStr.begin(); idx!=vecStr.end(); idx++) { cout << *idx << endl; } return 0; }
删除元素:
c.pop_back() 删除c中尾元素
c.pop_front() 删除c中首元素——同样,vector不支持
c.erase(p) 删除迭代器p指向的元素,返回一个执行被删元素之后元素的迭代器
c.erase(b, e) 删除迭代器b、e范围内元素
c.clear() 删除c中所有元素
使用erase 1:
#include <iostream> #include <vector> using namespace std; int main() { int num = 1; // vector<int> vec = {10, 1}; Error vector<int> vec(10, 2); vector<int>::iterator i; //vec.pop_back(); vec.insert(vec.begin(), 3, 3); vec.erase(vec.begin()); for(i = vec.begin(); i!=vec.end(); ++i) { cout << num++ << ": " << *i << endl; } return 0; }使用erase 2:
#include <iostream> #include <vector> using namespace std; int main() { int num = 1; // vector<int> vec = {10, 1}; Error vector<int> vec(10, 2); vector<int>::iterator i; //vec.pop_back(); vec.insert(vec.begin(), 3, 3); vec.erase(vec.begin(), vec.begin()+2); for(i = vec.begin(); i!=vec.end(); ++i) { cout << num++ << ": " << *i << endl; } return 0; }按理说上面的erase应该把3个3都删除了,可运行结果是还保留了一个3,原因应该是像迭代器的左闭右开区间:
erase(vec.begin(), vec.begin()+2); //不删除vec.begin()+2指向的元素
改变容器大小:
list<int> ilist(10, 20); //10个int 每个都是20 ilist.resize(15); //将5个值为0的元素添加到ilist末尾 ilist.resize(25, -1); //将10个值为-1的元素添加到ilist末尾 ilist.resize(5); //从ilist末尾删除20个元素
3.迭代器
标准容器类型上所有迭代器都允许我们访问容器中的元素,而所有迭代器都是通过解引用运算符来实现这个操作的。
迭代器范围:一个左闭右开区间[begin, end);
iterator、const_iterator、reverse_iterator、const_reverse_iterator;
获取容器迭代器:
vector<int> vec(10);//vec含10个元素,每个元素都是0 vector<int>::iterator i = vec.begin();//获取指向第一个元素的迭代器。 *i = 10;//解引用可以作为左值改变容器中的值
注意:向容器中添加元素和从容器中删除元素的操作可能会使指向容器元素的迭代器、指针、引用失效。因此必须保证每次改变容器的操作之后都能正确的重新定位迭代器。
4.vector是如何增长的?
标准库实现采用了可以减少容器重新分配次数的策略。当不得不获取新空间时,vector和string的实现通常会分配比新的空间需求更大内存空间。预留这些空间作为备用,可以用来保存更多的新元素。
capacity与size:
size是已经保存的元素的数目。capacity是在不分配新的内存空间下最多可以保存多少元素。capacity应该至少和size一样大。
例子:
vector<int> vec; cout << "vec.size: " << vec.size() << end; cout << "vec.capacity: " << vec.capacity() << end; for(int i = 0; i<24; i++) vec.pusk_back(i); cout << "vec.size: " << vec.size() << end; cout << "vec.capacity: " << vec.capacity() << end;
运行结果如下:
vec.size: 0
vec.capacity: 0
vec.size: 24
vec.capacity: 32
可以想象当前vec的状态:
[0] [1] [2] ...... [23] [.....保留空间.....]
c.reserve(n); //分配至少能容纳n个元素的内存空间
vec.reserve(50); cout << "vec.size: " << vec.size() << endl; cout << "vec.capacity: " << vec.capacity() << endl;结果:
vec.size: 0
vec.capacity: 0
vec.size: 24
vec.capacity: 50
课后习题:
#include <iostream> #include <vector> using namespace std; int main() { vector<string> vec; string s = "hello"; cout << "vec.size: " << vec.size() << endl; cout << "vec.capacity: " << vec.capacity() << endl; vec.reserve(1024); for(int i = 0; i<24; i++) { vec.push_back(s); } vec.resize(vec.size() + vec.size()/2);//当前size为24 24+12=36 因此当前size变为36 cout << "vec.size: " << vec.size() << endl; cout << "vec.capacity: " << vec.capacity() << endl; return 0; }