1.容器无关代码不存在,也就是不存在一种万能的容器,任何容器都有它存在的价值和用武之地.
2.因为容器类型可能会改变所以不要这么写:
class Widget {...};
vector<Widget> vw;
// ...
Widget bestWidget;
vector<Widget>::iterator i = find(vw.begin(), vw.end(), bestWidget);
而是这么写:
class Widget { ... };
typedef vector<Widget> WidgetContainer;
typedef WidgetContainer::iterator WCIterator;
WidgetContainer cw;
// ...
Widget bestWidget;
WCIterator i = find(cw.begin(), cw.end(), bestWidget);
3.如果你需要建立一个客户列表,请不要直接用list。取而代之的是,建立一个CustomerList类,把list隐藏在它的private区域:
class CustomerList
{
private:
typedef std::list<Customer> CustomerContainer;
typedef CustomerContainer::iterator CCIterator;
CustomerContainer customers;
public: // 通过这个接口
... // 限制list特殊信息的可见性
};
4.用empty来代替检查size()是否为0
if (c.size() == 0)... 写为if (c.empty())...
对于所有的标准容器,empty是一个常数时间的操作,但对于一些list实现,size花费线性时间, size或splice的区间形式——必须让步。即如果要保持一个size的常数,就要在splice的时候遍历一般获得新的size,或者保持splice是常数,就要保证获得list的size要遍历一遍数据,也就是一个或者另一个可以是常数时间操作,但不能都是。
5. 尽量使用区间成员函数代替它们的单元素兄弟,区间成员函数是一个像STL算法的成员函数,使用两个迭代器参数来指定元素的一个区间来进行某个操作,它通常比单元素操作要直观和高效。
区间构造:
所有标准容器都提供这种形式的构造函数:
container::container(InputIterator begin, // 区间的起点
InputIterator end); // 区间的终点
区间插入:
所有标准序列容器都提供这种形式的insert:
void container::insert(iterator position, // 区间插入的位置
InputIterator begin, // 插入区间的起点
InputIterator end); // 插入区间的终点
关联容器使用它们的比较函数来决定元素要放在哪里,所以它们了省略position参数:
void container::insert(lnputIterator begin, InputIterator end);
区间删除。
每个标准容器都提供了一个区间形式的erase,但是序列和关联容器的返回类型不同。
序列容器提供了这个:
iterator container::erase(iterator begin, iterator end);
关联容器提供这个:
void container::erase(iterator begin, iterator end);
区间赋值:
所有标准序列容器都提供了区间形式的assign:
void container::assign(InputIterator begin, InputIterator end);
6.删除元素
全局的remove算法:
template <class ForwardIterator, class T>
ForwardIterator remove(ForwardIterator first, ForwardIterator last,
const T& value);
这个算法会把[first,last)中不等于value的元素放到这段区域的最前面,但是不会改变容器的size,也就是后面会余下一定数目(与value相等元素的个数)的无效区域,并返回这个无效区域的first。为啥要这样,而不是把这些元素直接从容器中删除呢?
1.全局算法(非容器成员函数)不知道容器一些属性,比如容器的end,这个函数中的first和last不一定是容器的first和end,因此它也没权利去移动容器的后续元素去弥补这个无效区域。
因此对于vector、deque或string删除元素的最好方法是erase-remove惯用法:
c.erase(remove(c.begin(), c.end(), nValue), c.end());
c.erase(remove_if(c.begin(), c.end(), badValue), c.end());
对于list也可以这么做,但是list的成员函数remove更高效:
c.remove(nValue);
c.remove_if(badValue);
对于关联容器(set、multiset、map或multimap)应该是调用erase:
c.erase(nValue);
对于需要条件badValue的情况,需要遍历关联容器,然后一个一个删除。
遍历删除需要注意的:
A.如果容器是标准序列容器,写一个循环来遍历容器元素,每当调用erase时记得都用它的返回值更新你的迭代器。因为对于序列容器erase不仅使所有指向被删元素的迭代器失效,也使被删元素之后的所有迭代器失效。
SeqContainer<int> c;
// ...
for (SeqContainer<int>::iterator i = c.begin(); i != c.end();)
{
if (badValue(*i))
{
logFile << "Erasing " << *i << '\n';
// 标准序列容器erase返回被删元素之后的元素的有效迭代器。
i = c.erase(i);
}
else
{
++i;
}
}
B.如果容器是标准关联容器,写一个循环来遍历容器元素,当你把迭代器传给erase时记得后置递增它。
AssocContainer<int> c;
// ...
for (AssocContainer<int>::iterator i = c.begin(); i != c.end();)
{
if (badValue(*i))
{
// 标准关联容器的erase的返回类型是void。
c.erase(i++);
}
else
{
++i;
}
}
7. 使用“交换技巧”来修整过剩容量
vector<Contestant>(contestants).swap(contestants);
contestant.swap的作用和swap(constestantsA, constestantsB)是一样的。用于交换2个容器的内容。
template <class Assignable>
void swap(Assignable& a, Assignable& b);
8.避免使用vector<bool>
很多STL的实现对于vector<bool>并不保存真正的bool,而是打包bool以节省空间。在一个典型的实现中,每个保存在“vector”中的“bool”占用一个单独的比特,而一个8比特的字节将容纳8个“bool”。这将使很多操作比较危险。
有2个办法可以解决这个问题:
1. deque<bool>。deque提供了几乎所有vector所提供的(唯一值得注意的是reserve和capacity),而deque<bool>是一个STL容器,它保存真正的bool值。
2. 使用bitset。