条款12: 切勿依赖STL的线程安全性
- 对于一个STL,最多期望:
- 多个线程对容器的读是安全的;
- 多个线程对不同容器的写是安全的。
- 而且上述是最多期望这个,甚至有些stl中还不支持以上的内容。
所以程序员需要自己做同步控制:- 对一个容器的成员函数的调用,每次都锁住容器。
- 对于每个返回的迭代器的生存期间内,都锁住
- 对于算法的运行,锁住容器。
- 锁的使用可以参考Effective C++中使用一个类来实现。
条款13:vector和string优先于动态分配的数组
- 使用new操作符必须要注意以下三点:
- new之后一定要delete;
- new对应delete, new[] 对应 delete[];
- 确保只delete一次,delete多次会带来不确定的后果。
- 所以vector和string能够动态调整大小,并且能够自动管理内存。
- 只有一个情况特殊:在多线程环境下,string的引用计数机制可能会带来问题,而你需要做同步操作会使得效率较低。这时可以查手册,关闭string的引用计数功能,或者使用vector<char>,最后才是使用数组。
条款14:使用reserve来避免不必要的内存分配
- STL容器能够自动增长以容纳你放进来的元素,而对于vector或string,步骤如下:
- 分配一块当前大小2倍的大小(通常是2倍);
- 将所有元素复制过去;
- 析构旧的内存中的元素;
- 释放旧内存。
- 内存调整不仅影响效率,还会使得之前的迭代器、指针、引用都无效。所以尽早分配内存,使用reserve。
- 其他相关函数:
- size() 返回元素个数;
- capacity() 告诉你分配的内存可以容纳多少元素;
- resize(n) 强制将容器变为n个元素(非容量)的状态,多了则将尾部析构,不够则使用默认构造函数构造。
- reserve(n) 强制容量至少是n,至少的意思是如果n小于现容量则不变。
条款15:注意string实现的多样性
- 这个比较进阶,展示了string的多种实现方式。
- 总结:
- string可能引用计数也可能不是,一般可以选择关闭。
- string对象的大小可能从1到至少7倍char*指针的大小。
- 新字符串值的建立可能需要0、1或2次动态分配。
- …
条款16: 如何将vector或string对象传给遗留的C的API
-
比如一个老的C API:
void doSomething(const int* pInts, size_t numInts);
-
要将一个vector传入,则需要先判断v是否为空,然后取第一个元素的引用的地址:
if (!v.empty()) { doSomething(&v[0], v.size()); }
-
而char* ,string有个成员函数c_str(),转换为c风格的指针。
string s("asefa3ef"); doSomething(s.c_str());
条款17:使用swap技巧去除多余的容量
-
如下:
- 有一个vector contestants,可能会有多余的容量。
- 那么你建立一个临时变量,此变量以contestants为输入参数,调用拷贝构造函数,这时没有多余容量;
- 然后再使用swap函数交换,使得contestants中没有多余容量,而临时变量中有,但临时变量会自动析构。
class Contestant {...}; vector<Contestant> contestants; vector<Contestant>(contestants).swap(contestants);
-
需要注意:
- 1.这个不保证没有多余容量,因为可能有些容器会有默认最小的容量。
- 2.swap操作会让二者的迭代器、指针、引用等都改变,都被交换。
条款18: 避免使用vector<bool>
- 由于vector<bool>会优化,一个8bit的字节会有8个bool。
- 而对于引用等问题,采用复杂的代理对象技术。stl遗留问题。
- 替换可以使用deque<bool>,这是真的bool对象。或者非标准的bitset。
条款19:了解等价和相等的区别
-
相等的概念基于operator==。
-
等价: 两个对象x和y如果在关联容器c的排序顺序中没有哪个排在
另一个之前,那么它们关于c使用的排序顺序有等价的值。 -
一般set以less<T>作为判断,而 less 调用 < 符号。则如果二者等价,下列表达式为true:
!(w1 < w2) && !(w2 < w1)
-
而再通用的情况则是用户自定义一个比较函数。比如你要让一个字符串忽略大小写放进set。
struct CIStringCompare: public binary_function<string, string, bool> { // 关于这个基类的信息参见条款40 bool operator()(const string& lhs, const string& rhs) const { return ciStringCompare(lhs, rhs); // 关于ciStringCompare是怎么实现的参见条款35 } } set<string, CIStringCompare> ciss; // ciss = “case-insensitive string set”
-
CIStringCompare是一个仿函数类,operator()调用一个真正的函数来忽略大小写判断。
-
则等价判别式为:(虽然这里可以用)
!(CIStringCompare()(x, y)) && !(CIStringCompare()(y, x))
-
则在使用成员函数操纵时会使用等价性判断,而非成员函数比如find则会使用相等性来判断:
ciss.insert("Persephone"); // 一个新元素添加到set中 ciss.insert("persephone"); // 没有新元素添加到set中 if (ciss.find("persephone") != ciss.end())... // 这个测试会成功 if (find(ciss.begin(), ciss.end(), "persephone") != ciss.end())... // 这个测试会失败
条款20:为元素是指针的关联容器指定比较类型
- 元素为指针的话,默认排列会按照指针的less来进行比较,而不是指针中的value值来排序,那么就需要你去写一个取出值来判断。
// 使用这个基类的理由参见条款40 struct StringPtrLess: public binary_function<const string*, const string*, bool> { bool operator()(const string *ps1, const string *ps2) const{ return *ps1 < *ps2; } };
- 然后就可以指定这个函数类的set:
set<string*, StringPtrLess> ssp;StringPtrSet ssp; // 按照StringPtrLess定义的顺序排序
- 为什么要写一个函数类而不是一个函数,是因为set不能以函数来定义。
条款21: 永远让等价性比较函数对相等的值返回false
- 由于等价性是由以下的式子得出的结果,那么如果相等的值返回true,则整个式子返回false,这是不对的。
!(Compare()(x, y)) && !(Compare()(y, x))