条款7
class SpecialString: public string {....}
这种行为很危险,因为string像所有的标准STL容器,缺少虚席够函数,从而没有虚析构函数的类共有继承是一个大的c++禁忌。
条款8
typedef typename iterator_traits<RandomAccessIterator>::value_type ElementType;
涉及在模板中到模板类或普通类中自定义的类型时,需要用typename 来修饰,否则c++ 编译器把value_type 当做静态数据。
条款9
关联容器 AssocContainer<int> c;
for (AssocContainer<int>::iterator i = c.begin();
i != c.end();
i++)
{
if (badVallue(*i))
c.erase(i);
}
清晰但漏洞百出,删除c 中的badvalue 返回真的每个元素,当c.erase(i)后, i 迭代器失效,但却要执行 ++i,这是个坏消息。解决方案
for (AssocContainer<int>::iterator i = c.begin();
i != c.end(); )
{
if (badVallue(*i))
c.erase(i++);
else
++i;
}
为vector,string,deque这样的容器调用erase不仅使所有指向被删呀unsede迭代器失效,也是被删元素之后的所有迭代器失效。 我们这里写的 i++,++i 都不能导致迭代器有效。(deque 可能特殊)
for (SeqContainer<int>::iterator i = c.begin(); i != c.end();){ if (badValue(*i)){ logFile << "Erasing " << *i << '\n'; i = c.erase(i); // 通过把erase的返回值 } // 赋给i来保持i有效 else ++i; }
你可以像vector/string/deque一样或像关联容器一样对待list;两种方法都可以为list工作。
两种情况:一般vector,string,deque 在erase时使当前迭代器和其后的迭代器都失效,而关联容器仅仅使当前迭代器失效。
条款10
- 把你的分配器做成一个模板,带有模板参数T,代表你要分配内存的对象类型。
- 提供pointer和reference的typedef,但是总是让pointer是T*,reference是T&。
- 决不要给你的分配器每对象状态。通常,分配器不能有非静态的数据成员。
- 记得应该传给分配器的allocate成员函数需要分配的对象个数而不是字节数。也应该记得这些函数返回T*指针(通过pointer typedef),即使还没有T对象被构造。
- 一定要提供标准容器依赖的内嵌rebind模板。
记得要看:Austern的文章《What Are Allocators Good For?》
条款11
关于共享内存的对象
mallocShared、freeShared
placement new 的用法
new(void* pT)T(initValue);
条款12
STL容器线程安全:
多个读取者是安全的。
对不同容器的多个写入者是安全的。多个线程可以同时写不容的容器
STL容器不是线程安全的,要实现线程安全的STL库很难,只能交给用户去负担。
vector<int> v; vector<int>::iterator first5(find(v.begin(), v.end(), 5)); // 行1 if (first5 != v.end()){ // 行2 *first5 = 0; // 行3 }
会出现的情况:
1 行2对first5 和 v.end 的检测将是无意义的
2 另一线程插在行1 和行2 之间,使 first5 失效,造成 vector重新分配它的内在内存。
3 行3对 *first5 的赋值是不安全的,应为另一个线程可能在行2 和行 3 之间执行,使 first5 失效,已经删除指向它的元素。
要让上面的代码成为线程安全的,v必须从行1到行3保持锁定。
关于互斥:
手工版:
vector<int> v; ... getMutexFor(v); vector<int>::iterator first5(find(v.begin(), v.end(), 5)); if (first5 != v.end()) { // 这里现在安全了 *first5 = 0; // 这里也是 } releaseMutexFor(v);
工业级版:
template<typename Container> // 获取和释放容器的互斥量 class Lock { // 的类的模板核心; public: // 忽略了很多细节 Lock(const Containers container) : c(container) { getMutexFor(c); // 在构造函数获取互斥量 } ~Lock() { releaseMutexFor(c); // 在析构函数里释放它 } private: const Container& c; };
vector<int> v; ... { // 建立新块; Lock<vector<int> > lock(v); // 获取互斥量 vector<int>::iterator first5(find(v.begin(), v.end(), 5)); if (first5 != v.end()) { *first5 = 0; } } // 关闭块,自动 // 释放互斥量
关于两者的区别:
这种基于Lock的方法在有异常的情况下是稳健的。C++保证如果抛出了异常,局部对象就会被销毁,所以即使当我们正在使用Lock对象时有异常抛出,Lock也将释放它的互斥量。如果我们依赖手工调用getMutexFor和releaseMutexFor,那么在调用getMutexFor之后releaseMutexFor之前如果有异常抛出,我们将不会释放互斥量。