C2@vector & string
reserve()与resize()的区别在于:reserve()仅仅会为你分配一段未经初始化的内存空间,容器内元素的个数也即容器的size()并不会改变,使用reserve()的初衷在于当容器的使用情况可预知时,提前调用reserve()预分配空间可避免reallocation所带来的沉重开销;resize()的行为是分配一段已初始化的空间,容器内元素的个数(Container.size()以及Container.end())随之改变,resize()的初衷在于一些基于assignment的STL算法只能对已经初始化完成的(也即constructor()已被调用的)内存空间施加操作。
std::string包含的成员可能会包括,
@size & @capacity;
@pointer & @allocator: 字符串的实际分配是经由Allocator通过动态分配实现的,allocator会作为具象实例存在于每个string中;
@reference count: 从节省空间的角度考虑,很多的库都会含有该成员。当需要使用某字符串例如”hello cplusplus”时,程序会动态分配一份,接下来所有使用该字符串的string仅仅通过@pointer指向该字符串,其被引用的个数记录在@reference count中,@reference count减为0时动态进行销毁。
基于这些成员可以得知,为什么sizeof(string)会出人意料的大。
C3@Associative Container
在STL中有两种“相等”:一种是algorithm::find()所定义的相等,基于operator==()实现;另一种则是std::set/map所定义的相等,基于operator<()实现。二者都用于查找,直观来看operator==()足够使用,而之所以operator<()会被使用在于set/map是有序的,从有序的容器中查找元素,基于operator<()的二分法能够提供远优于线性查找的性能。
当然,利用operator<()定义的“相等”可能会让人觉得过于繁琐 A == B <--> !operator<(A, B)&& !operator<(B, A),翻译一下就是A不小于B同时B不小于A。熟悉二分法的朋友或许会问,何不将operator==()与operator<()混在一起使用,毕竟二分法中是先判断”if(B == mid)”,当不等时才会引入”<”。问题在于这就需要operator==()和operator<()的定义保持一致性,考虑一个字符串的比较,如果operator==()假定大小写不敏感而operator<()假定大小写敏感,这个查找就会出现问题。换句话来讲,混合使用可能会引入定义的不一致进而导致程序出现问题。
最后,当容器中存储的是你声明的Class时,记得定义自己的operator==(), operator<()。
C4@Iterator
鉴于很多container的成员函数要求输入参数是iterator而将const_iterator拒之门外,所以在很多情况下const_iterator的使用者就必要为手里的const_iterator摘掉const的帽子。卸帽工具?第一反馈该是const_cast<>,不过让人略感意外的是…这直观的方式在多数情况下(string, vector除外)会难以通过编译。
问题的原因在于对大多数container(包括list, deque, set,multiset, map, multimap)而言,它们的iterator和const_iterator实现源于不同的类,通过const_cast<>完成不同class的转化,Bjarne的口味应该不会这么重。Scott给出的解决方案是引入advance()和distance():
std::advance ( iter ascontainer.begin(),
std::distance<const_type>( container.begin(), const_target_iter )
至于前文提到的例外-- string和vector,const_cast<>是可以起作用的,原因在于这两个容器里pointer和iterator是等价的,即为
vector<T>::iterator等价于T*, 同时vector<T>::const_iterator等价于const T*。自然const_cast<>刚好够用。