6.1 容器的共通能力和共通操作
所有容器提供的都是value语意而非reference语意。容器元素必须能够被拷贝。
总体而言,所有元素形成一个次序。运用迭代器遍历,也是算法的基础。
各项操作并非安全,调用者必须确保参数符合要求。违反可能导致未定义行为。通常STL自身不抛出异常。
初始化:每个容器提供了一个默认构造函数、copy构造函数、析构函数。
容器类别的共通函数:
C c; 不含任何元素的空容器
C c(c1); 产生一个同型容器
C c(beg, end); 复制区间元素,作为容器初值
c.~C(); 删除所有元素,释放内存
c.size(); 返回容器元素数量
c.empty(); 若容器为空返回true
c.max_size(); 返回容器内元素最大可能数量
c1 == c2 ;
c1 != c2 ; > < >= <=
c1 = c2; 将c2的所有元素赋值给c1
c1.swap(c2); 交换c1和c2的数据
swap(c1, c2); 同上,全局函数
c.begin(); 返回指向第一个元素迭代器
c.end(); 返回指向最后元素的下个位置的迭代器
c.rbegin(); 返回指向逆向遍历时候的第一个元素的逆向迭代器
c.rend(); 返回指向逆向遍历时候的最后元素的下个位置的逆向迭代器
c.insert(pos, elem); 将elem的一份副本插入pos处,返回值和pos意义并不同
c.erase(beg, end); 移除区间内所有元素
c.clear(); 移除所有元素
c.get_allocator(); 返回容器的内存模型
以另一个容器的元素为初值完成初始化:
list<int> l;
vector<float> c(l.begin(), l.end());
以某个数组元素为初始初始化:
int a[] = { 2, 3, 17, 33, 45, 77 };
set<int> c(a, a + sizeof(a)/sizeof(*a));
标准输入完成初始化:
deque<int> c((istream_iterator<int>(cin)),(istream_iterator<int>())); //括号不能少,否则视为c为一个函数声明
6.2 vector : 动态数组
#include <vector>
具备assignable 和 copyable两个性质
namespace std {
template <class T, class Allocator = allocator<T> >
class vector;
}
//第二个参数一般省略,缺省是C++ STL提供的allocator分配器
vector是有序群集,支持随机存取,随机存取迭代器,任何STL算法都适用。
末端附加和删除元素时候,性能好。前端或中间插入和删除元素性能不好。因为要移动操作点之后的所有元素,这是很多 = 赋值操作符。
capacity(); 返回vector实际能容纳的元素数量,如果超越这个数量vector就会重新配置内部存储器。
一旦内存重新配置,和vector元素有关的所有reference、pointers、iterator都会失效,内存重新配置很耗时间。
可以使用reserve()函数保留适当容量,只能增不能减,避免总是重新配置内存,避免iterator失效。 vector<int> v; v.reserve(80); 分配了80个元素的v
还可以在构造期间分配大小: vector<int> v(100); //v 起始大小100个元素
构造和析构函数:
vector<Elem> c;
vector<Elem> c1(c2);
vector<Elem> c(n);
vector<Elem> c(n, elem);
vector<Elem> c(beg, end);
c.~vector<Elem>();
非变动性操作:
c.size();
c.empty();
c.max_size();
capacity();
reserve(n);
c1 == c2; != < > <= >=
赋值:
c1 = c2;
c.assign(n, elem);
c.assign(beg, end);
c1.swap(c2);
swap(c1, c2);
元素存取:index 从0到size()-1; 对于non-const vector返回元素的reference;程序员自己检查范围例如是否为空;
c.at(idx); //out_of_range 异常
c[idx]; 不进行范围检查
c.front();
c.back();
迭代器相关:其实就是个指针,vector内部结构是数组
c.begin(); //随机存取迭代器
c.end();
c.rbegin(); //逆向迭代器
c.rend();
迭代器失效情况:较小的索引位置插入或移除元素;容量变化重新引起内存分配
插入和移除元素使作用点之后的元素迭代器失效,甚至引发内存重新分配届时所有迭代器失效;
移除插入相关操作:
c.insert(pos, elem); //返回新元素位置
c.insert(pos, n, elem); //无返回值
c.insert(pos, beg, end); //无返回值
c.push_back(elem);
c.pop_back();
c.erase(pos); //返回下一个元素的位置
c.erase(beg, end); //返回下一个元素的位置
c.resize(num); 将元素数量改为num,如果size()增大了,多出新元素需要默认构造函数完成
c.resize(num, elem);
c.clear(); 容器清空
vector<Elem> c;
c.erase(remove(c.begin(), c.end(), val), c.end()); //将所有其值为val的元素移除
#include <algorithm>
#include <vector>
#include <iostream>
#include "print.cpp"
using namespace std;
int main()
{
int a[] = {3, 4, 5, 3, 7, 9, 3, 4};
vector<int> v(a, a+sizeof(a)/sizeof(*a));
v.erase(remove(v.begin(), v.end(), 3), v.end());
print_elements(v, "after erase: "); //4 5 7 9 4
return 0;
}
若想移除与某值相等的第一个元素:
#include <algorithm>
#include <vector>
#include <iostream>
#include "print.cpp"
using namespace std;
int main()
{
int a[] = {3, 4, 5, 3, 7, 9, 3, 4};
vector<int> v(a, a+sizeof(a)/sizeof(*a));
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
if(pos != v.end())
{
v.erase(pos);
}
print_elements(v, "after erase: "); //4 5 3 7 9 3 4
return 0;
}
将vector当作一般array使用:
&v[i] = &v[0] + i;
vector<char> v;
v.resize(40);
strcpy(&v[0], "hello, world");
printf("%s\n", &v[0]); //printf("%s\n", v.begin()); is error
异常处理:
下标操作的安全版本:at()
push_back()安插元素时候发生异常,插入不起作用
若元素拷贝不抛出异常,insert要么成功要么不生效
pop_back()不抛异常
若拷贝操作不抛异常,erase()和clear()不抛异常
swap()不抛异常
若拷贝操作绝对不抛异常,那么所有操作要么成功要么不起作用。
析构函数不抛异常
一个例子总结vector:
#include <algorithm>
#include <vector>
#include <iostream>
#include "print.cpp"
#include <string>
using namespace std;
int main()
{
vector<string> s;
s.reserve(5);
s.push_back("Hello, ");
s.push_back("how ");
s.push_back("are ");
s.push_back("you ");
s.push_back("?");
print_elements(s, "init: "); //init: Hello, how are you ?
cout << "max_size(): " << s.max_size() << endl; //1073741823
cout << "size(): " << s.size() << endl; //5
cout << "capacity(): " << s.capacity() << endl; //5
swap(s[1], s[3]);
s.insert(find(s.begin(), s.end(), "?"), "always");
s.back() = "!";
print_elements(s, "after: "); //after: Hello, you are how always !
cout << "max_size(): " << s.max_size() << endl; //1073741823
cout << "size(): " << s.size() << endl; //6
cout << "capacity(): " << s.capacity() << endl; //10
return 0;
}