条款十三
vector与string优于动态分配数组
不必使用delete也不必担心delete两次相同的指针
条款十四
使用reserve来避免不必要的内存重分配
vector与string的内存扩张
1、分配两倍的空间
2、拷贝(可以的话会移动)原来的元素到新的空间
3、释放原来的空间(拷贝的话需要先析构原来的元素)
相关函数
size()个数
capacity()返回最大容量
size() == capacity;时的下一次插入引起内存重新分配
resize(n)将容器变到容纳n个元素的状态,n<size裁剪,n>size填充(默认构造或给定值),n>capacity()重新分配内存。
reserve(n)重配内存,n小于当前容量时vector忽略操作,string调整到size与n的较大者;大于时可能重配(如果可以)。
条款十五
string实现
(1)
包含:
size即字符个数
capacity即容量
value即实际的字串
可能包含:
分配子的拷贝
引用计数
A类型:
无alloc优化
4倍与指针,1次动态分配
结构:
allocator
size
capacity
value pointer -> refcount(引用计数) + 实际字串
B类型
等于指针,两次动态分配
结构:
指针 -> size
(可能alloc) capacity
refcount
pointer -> 实际字串
同步控制(6倍于指针大小)
size + capacity + refcount + pointer = 4倍于指针的大小
C类型
等于指针,一次动态分配
pointer -> size
capacity
refcount
x(共享性相关的数据)
value
即C类型不支持自定义allocator,所有string使用同一个分配子
D类型
7倍于一个普通指针,小于15时不需要动态分配(即小字符串优化)
allocator
pointer -> value
足够容纳15字符的空间
size
capacity
无需多线程控制,因为并没有引用计数
共享性D < A < B与C
(2)
将vector与string传给旧的API
&vector[0]
&(*vector.begin())
string.c_str();
对于string,C API不能对其进行改变
对于vector则为不能改变元素数量,因为vector本身无法察觉这些变化(start last finish三个指针并不会跟着传递到API里)
若要在API内修改:
vector:
使用resize( fun(&vector[0],size ) 让fun返回新size来使得vector进行改变。
string:因为vector与string可以相互用来初始化,所以先把string传到vector里,然后再按照vector的方法改变vector,然后再用改变号的vector去初始化原来的string。
条款十七
使用swap除去多余的容量
vector (orginal).swap(orginal);
使用orginal作为副本创建一个无名的临时vector对象,这个对象的容量等于prginal,然后把这个对象和orginal进行交换达到除去orginal中多余容量的作用。(其实做到的是当前实现下的最小,即减小到用拷贝构造函数构建的容器的大小)
条款十八
避免使用vector<bool>
vector<bool>的[]返回的是一个代理对象,用以模拟bool的行为,即不是真正的bool类型。
只有type *p = &vector<type>[0]成立才是容器,也就是说vector<bool>不算是真正的容器
deque可以代替,或者bitset
条款十九
理解等价与相等的不同
非成员函数:
find基于 operator== 即相等,成员版本的find则基于等价,即operator<(!(a<b) && !(b<a)成立即等价)
insert基于operator<,即等价
关联容器的等价检测
a,b
return !(a<b)
&&
!(b<a);
用户定义的类型比较
return !key_compare()(a,b)
&&
!key_compare()(b,a);
key_compare()返回用户定义的函数对象(或用以比较的函数对象)
set<type,comp>
comp要转为可调用对象
条款二十
为包含指针的关联容器指定比较类型
因为关联容器在未指定时是会使用指针内的地址值作为比较准则的,智能指针与迭代器同理,要根据它们指向的值来排序的话需要自己传入比较子。
条款二十一
总是让比较函数在相等是返回false
否则关联容器会把两个相等的值判断为不等价(根据之前的比较规则),那么当你用范围取出函数的时候就不能得到这两个等价的结果了。