1.
不要这么写:
class Widget {...};
vector<Widget> vw;
Widget bestWidget;
... // 给bestWidget一个值
vector<Widget>::iterator i = // 寻找和bestWidget相等的Widget
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);
2.
要限制如果用一个容器类型替换了另一个容器可能需要修改的代码,就需要在类中隐藏那个容器,而且要通
过类的接口限制容器特殊信息可见性的数量。比如,如果你需要建立一个客户列表,请不要直接用list。取而
代之的是,建立一个CustomerList类,把list隐藏在它的private区域:
class CustomerList {
private:
typedef list<Customer> CustomerContainer;
typedef CustomerContainer::iterator CCIterator;
CustomerContainer customers;
public: // 通过这个接口
... // 限制list特殊信息的可见性
};
3.
一个使拷贝更高效、正确而且对分割问题免疫的简单的方式是建立指针的容器而不是对象的容器。
4.
用empty来代替检查size()是否为0,理由很简单:对于所有的标准容器,empty是一个常数时间的操作,但对于一
些list实现,size花费线性时间。
5.
记得delete容器里面分配的指针。
6.
永不建立auto_ptr的容器
7.
在删除选项中仔细选择
你想把c中所有值为1963的对象都去掉。令人吃惊的是,完成这项任务的方法因不同的容器类型而不同:没
有一种方法是通用的。
如果你有一个连续内存容器(vector、deque或string——参见条款1),最好的方法是erase-remove惯用法(参
见条款32):
c.erase(remove(c.begin(), c.end(), 1963), // 当c是vector、string
c.end()); // 或deque时,
// erase-remove惯用法
// 是去除特定值的元素
// 的最佳方法
这方法也适合于list,但是,正如条款44解释的,list的成员函数remove更高效:
c.remove(1963); // 当c是list时,
// remove成员函数是去除
// 特定值的元素的最佳方法
如果我们观察在本条款中提到的所有东西,我们得出下列结论:
● 去除一个容器中有特定值的所有对象:
如果容器是vector、string或deque,使用erase-remove惯用法。
如果容器是list,使用list::remove。
如果容器是标准关联容器,使用它的erase成员函数。
● 去除一个容器中满足一个特定判定式的所有对象:
如果容器是vector、string或deque,使用erase-remove_if惯用法。
如果容器是list,使用list::remove_if。
如果容器是标准关联容器,使用remove_copy_if和swap,或写一个循环来遍历容器元素,当你把迭代
器传给erase时记得后置递增它。
● 在循环内做某些事情(除了删除对象之外):
如果容器是标准序列容器,写一个循环来遍历容器元素,每当调用erase时记得都用它的返回值更新你
的迭代器。
如果容器是标准关联容器,写一个循环来遍历容器元素,当你把迭代器传给erase时记得后置递增它。
8.
对STL容器线程安全性的期待现实一些
● 多个读取者是安全的。多线程可能同时读取一个容器的内容,这将正确地执行。当然,在读取时不能
有任何写入者操作这个容器。
● 对不同容器的多个写入者是安全的。多线程可以同时写不同的容器。
9.
尽量使用vector和string来代替动态分配的数组
这一刻,你决定使用new来进行动态分配,你需要肩负下列职责:
1. 你必须确保有的人以后会delete这个分配。如果后面没有delete,你的new就会产生一个资源泄漏。
2. 你必须确保使用了delete的正确形式。对于分配一个单独的对象,必须使用“delete”。对于分配一个
数组,必须使用“delete []”。如果使用了delete的错误形式,结果会未定义。在一些平台上,程序在
运行期会当掉。另一方面,它会默默地走向错误,有时候会造成资源泄漏,一些内存也随之而去。
3. 你必须确保只delete一次。如果一个分配被删除了不止一次,结果也会未定义。
10.
使用reserve来避免不必要的重新分配
vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector的capacity同时也增加了它的size!
原因如下:
reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素。加入新的元素时,要调用push_back()/insert()函数。
resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。此时再调用push_back()函数,是加在这个新的空间后面的。
两个函数的参数形式也有区别的,reserve函数之后一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小, 第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。
11.
使用“交换技巧”来修整过剩容量
交换技巧的变体可以用于清除容器和减少它的容量到你的实现提供的最小值。你可以简单地和一个默
认构造的临时vector或string做个交换:
vector<Contestant> v;
string s;
... // 使用v和s
vector<Contestant>().swap(v); // 清除v而且最小化它的容量
string().swap(s); // 清除s而且最小化它的容量
12.
为指针的关联容器指定比较类型
你应该回忆起
set<string*> ssp;
是这个的简写:
set<string*, less<string*> > ssp;
好,为了完全准确,它是
set<string*, less<string*>, allocator<string*> > ssp;
的简化,但是分配器在本条款里与我们无关,所以我们将忽略它们。
如果你想要string*指针以字符串值确定顺序被储存在set中,你不能使用默认比较仿函数类less<string*>。你必
须改为写你自己的比较仿函数类,它的对象带有string*指针并按照指向的字符串值来进行排序。就像这样:
struct StringPtrLess:
public binary_function<const string*, // 使用这个基类
const string*, // 的理由参见条款40
bool> {
bool operator()(const string *ps1, const string *ps2) const
{
return *ps1 < *ps2;
}
};
然后你可以使用StringPtrLess作为ssp的比较类型:
typedef set<string*, StringPtrLess> StringPtrSet;
StringPtrSet ssp; // 建立字符串的集合,
// 按照StringPtrLess定义的顺序排序
如果你想要改为使用算法,你可以写一个知道怎么在打印string*指针之前对它们解引用的函数,然后和
for_each联用那个函数:
void print(const string *ps) // 把ps指向的
{ // 对象打印到cout
cout << *ps << endl;
}
for_each(ssp.begin(), ssp.end(), print); // 在ssp中的每个
// 元素上调用print
或者你想象并写出了泛型的解引用仿函数类,然后让它和transform与ostream_iterator连用:
// 当本类型的仿函数被传入一个T*时,它们返回一个const T&
struct Dereference {
template <typename T>
const T& operator()(const T *ptr) const
{
return *ptr;
}
};
transform(ssp.begin(), ssp.end(), // 通过解引用“转换”
ostream_iterator<string>(cout, "\n"), // ssp中的每个元素,
Dereference()); // 把结果写入cout
13.
对于sort,永远让比较函数对相等的值返回false
14.
对map中的元素更新用operator[],插入用insert
15.
尽量用iterator代替const_iterator,reverse_iterator和
const_reverse_iterator
16.
用distance和advance把const_iterator转化成iterator
17.
通过reverse_iterator的base得到iterator
18.
需要一个一个字符输入时考虑使用istreambuf_iterator
实例:
ifstream infile("a.txt");
istreambuf_iterator<char> start(infile);
istreambuf_iterator<char> eof;
string str(start, eof);
cout << str << endl;
19.
算法:
总结一下你的排序选择:
● 如果你需要在vector、string、deque或数组上进行完全排序,你可以使用sort或stable_sort。
● 如果你有一个vector、string、deque或数组,你只需要排序前n个元素,应该用partial_sort。
● 如果你有一个vector、string、deque或数组,你需要鉴别出第n个元素或你需要鉴别出最前的n个元素,
而不用知道它们的顺序,nth_element是你应该注意和调用的。
● 如果你需要把标准序列容器的元素或数组分隔为满足和不满足某个标准,你大概就要找partition或
stable_partition。
● 如果你的数据是在list中,你可以直接使用partition和stable_partition,你可以使用list的sort来代替sort和
stable_sort。如果你需要partial_sort或nth_element提供的效果,你就必须间接完成这个任务,但正如我
在上面勾画的,会有很多选择。
另外,你可以通过把数据放在标准关联容器中的方法以保持在任何时候东西都有序。你也可能会考虑标准非
STL容器priority_queue,它也可以总是保持它的元素有序。
20.
如果你真的想删除东西的话就在类似remove的算法后接上erase
21.
通过mismatch或lexicographical比较实现简单的忽略大小写字
符串比较
22.
尽量用算法调用代替手写循环
23.
尽量使用内置方法(例如find,sort)
24.
考虑使用函数对象代替函数作算法的参数