条款6:警惕C++最令人恼怒的解析
参数为double变量
int f(double d);
int f(double (d)); // 同上;d左右的括号被忽略
参数为函数指针int f(double); // 同上;参数名被省略
int g(double (*pf)()); // g带有一个指向函数的指针作为参数
int g(double pf()); // 同上;pf其实是一个指针
int g(double ()); // 同上;参数名省略
以下写法是错误的:
ifstream dataFile("ints.dat"); list<int> data(istream_iterator<int>(dataFile), // 警告!这完成的并不 istream_iterator<int>()); // 是像你想象的那样
如果出现这个函数:
list<int> data(istream_iterator<int>(dataFile), istream_iterator<int>());第一个参数类型istream_iterator<int>
第二个参数类型是返回istream_iterator<int>的函数指针
解决方法:
ifstream dataFile("ints.dat"); istream_iterator<int> dataBegin(dataFile); istream_iterator<int> dataEnd; list<int> data(dataBegin, dataEnd);
条款7:当使用new得指针的容器时,记得在销毁容器前delete那些指针
容器很智能,在容器销毁时(例如:函数中声明的局部变量容器)会自动删除容器内所有数据,但不包括指针。如果传入的是new进去的指针,会产生大量的内存泄露,因为指针的“析构函数”是无操作!它肯定不会调用delete。
解决方法:
1.显式调用delete
2.
template<typename T> struct DeleteObject : // 条款40描述了为什么 public unary_function<const T*, void> { // 这里有这个继承 void operator()(const T* ptr) const { delete ptr; } };
现在你可以这么做:
void doSomething() { ... // 同上 for_each(vwp.begin(), vwp.end(), DeleteObject<Widget>); }这话中方法存在不少问题,比如有人恶意从string继承,string和所有的标准STL容器一样缺少"虚"析构函数。
如果这样:
struct DeleteObject { // 删除这里的 // 模板化和基类 template<typename T> // 模板化加在这里 void operator()(const T* ptr) const { delete ptr; } }就可以解决上面的那个问题,但是如果之间异常也会出现内存泄露,所以!
void doSomething() { typedef boost::shared_ ptr<Widget> SPW; //SPW = "shared_ptr // to Widget" vector<SPW> vwp; for (int i = 0; i < SOME_MAGIC_NUMBER; ++i) vwp.push_back(SPW(new Widget)); // 从一个Widget建立SPW, // 然后进行一次push_back ... // 使用vwp } // 这里没有Widget泄漏,甚至 // 在上面代码中抛出异常看来看去看不懂。
对了,要避免suto_ptr的容器来形成可以自动删除的指针。
条款8:永不建立auto_ptr的容器
使用auto_pt的容器页就是COAPs在C++标准里面是不被认可的。
如果这么做:
auto_ptr<Widget> pw1(new Widget); // pw1指向一个Widget auto_ptr<Widget> pw2(pw1); // pw2指向pw1的Widget; // pw1被设为NULL。(Widget的 // 所有权从pw1转移到pw2。) pw1 = pw2; // pw1现在再次指向Widget; // pw2被设为NULL如果容器进行sort排序的话就会出现一些指针变成NULL
条款9:在删除选项中仔细选择、
想把容器中所有1963对象都删除
连续内存容器(vector、deque、string)使用erase-remove
c.erase(remove(c.begin(), c.end(), 1963), // 当c是vector、string c.end()); // 或deque时, // erase-remove惯用法 // 是去除特定值的元素 // 的最佳方法list使用
c.remove(1963); // 当c是list时, // remove成员函数是去除 // 特定值的元素的最佳方法对于关联容器
c.erase(1963); // 当c是标准关联容器时 // erase成员函数是去除 // 特定值的元素的最佳方法消除下面判断式:
bool badValue(int x); // 返回x是否是“bad”
序列容器:
c.erase(remove_if(c.begin(), c.end(), badValue), // 当c是vector、string c.end()); // 或deque时这是去掉 // badValue返回真 // 的对象的最佳方法 c.remove_if(badValue); // 当c是list时这是去掉 // badValue返回真 // 的对象的最佳方法标准关联容器:
AssocContainer<int> c; // c现在是一种 ... // 标准关联容器 AssocContainer<int> goodValues; // 用于容纳不删除 // 的值的临时容器 remove_copy_if(c.begin(), c.end(), // 从c拷贝不删除 inserter(goodValues, // 的值到 goodValues.end()), // goodValues badValue); c.swap(goodValues); // 交换c和goodValues // 的内容
条款10:注意分配器的协定和约束
条款11:理解自定义分配器的正确用法
条款12:对STL容器线程安全性的期待现实一些
关于分配器,后边会有单独一篇来介绍,我对分配器的理解还在粗浅阶段,《Effective STL》里面的条款10-11无法理解。
对于多线程以及STL容器线程安全性同样感到困惑,暂时放下。期待后面解决
—— by Gustav 2013-7-18 9:39:14