不要把auto_ptr放入容器
原来没有规划本身翻译Effective STL的,怕影响各人情绪:),只是发明有些条款在网络上找不到,只好本身翻译了。–Winter
率直的讲,这个条款不该插手本书,因为包括atuo_ptr的容器(COAPs)原来在STL中就是克制的。就算你这么用了,编译器也不会编译你的代码。而尺度委员会也没有表明为什么不能这样。对付COAPs我应该什么都不消说的,因为你的编译器应该替你做好了一切事情,所有这种雷同的代码都不该该被编译。
惋惜的是,很多措施员利用的STL版本并不拒绝COAPS,更要命的是,很多措施员发明这个曾经很是简朴直接用于淘汰内存泄漏的自动指针常常陪伴指针容器。因此固然STL大概并不支持,很多措施员照旧去试图利用COAPs。
我来讲授COAPs到底有什么缺点,以至于尺度委员会都明晰克制它。好了,顿时开始。我讲一个不敷点,不消auto_ptr的相关常识,甚至不消任何STL容器的常识你都能大白:COAPs不机动(portable)。他们到底能奈何?C++尺度中就克制它了,好一点的STL平台将都不容许它的存在,这些都是有原因的,我会逐步汇报你。当前的STL平台没有克制COAPs,你会发明存在COAPs时,代码比以前越发不机动了。只要你很看中机动性(虽然应该如此),你就应该拒绝利用COAPs。
可是大概你并不体贴机动性,假如然是这样,请答允我友善的提醒你,就是拷贝atuo_ptr的奇特的(也可以说成是奇怪的)界说。
当你拷贝一个auto_ptr,被auto_ptr所指的工具的所有权已经转移到了新的auto_ptr中去了,原有的auto_ptr被配置为NULL。你领略的没有错:拷贝一个auto_ptr,将会改变auto_ptr自己的值。
auto_ptr<Widget> pw1 (new Widget); // pwl1points to a Widget
auto_ptr<Widget> pw2(pw1); // pw2 points to pw1's Widget;
// pw1 is set to NULL. (Ownership
// of the Widget is transferred
//from pw1 to pw2.)
pw1 = pw2; // pw1 now points to the Widget
// again; pw2 is set to NULL
这大概很有趣,但虽然是不正常的,你作为一个STL的利用者,要你小心利用的原因是,auto_ptr会导致一些莫名其妙的功效。举例来说,具体看看下面看似“清白无辜”的代码,这些代码先发生一个包括atuo_ptr<Widget>的vector,然后通过较量指向Widgets的指针来对vector举办排序:
bool widgetAPCompare(const auto_ptr<Widget>& lhs,
const auto_ptr<Widget>& rhs) {
return *lhs < *rhs; //for this example, assume that
} // operator< exists for Widgets
vector<auto_ptr<Widget> > widgets; // create a vector and then fill it
//with auto_ptrs to Widgets;
// remember that this should
//not compile!
sort(widgets.begin(), widgets.end(), // sort the vector
widgetAPCompare);
不管这些代码看起来有何等公道,这个运行功效必定是不公道的。最简朴的,排序的时候,widgets中会有一个可能多个auto_ptrs的值被改为NULL。vector自己的排序操纵,却改变了容器自己的值。把这个中的道理弄大白照旧较量值得的,我们来揣度一下:
它大概是因为sort算法的实现(一个公用的要领,并被证明过的),在快速排序算法进程中不得不利用一些姑且变量。我们并不体贴快速排序算法的详细实现,但其根基观念是,排序一个容器中的元素,肯定会有某个元素用来作为“pivot element”,然会递归较量一个元素比这个pivot element大于小于或便是举办排序。在排序内部,实现要领大概像这样:
template<class RandomAccesslterator, // this declaration for
class Compare> // sort is copied straight
void sort( RandomAccesslterator first, // out of the Standard
RandomAccesslterator last,
Compare comp)
{
// this typedef is described below
typedef typename iterator_traits<RandomAccesslterator>::value_type
ElementType;
RandomAccesslterator i;
… // make i point to the pivot element
ElementType pivotValue(*i); //copy the pivot element into a
// local temporary variable; see
//discussion below
… //do the rest of the sorting work
}
除非你常常阅读STL源代码,不然你大概对这样的代码感想惊骇,其实,这些并不真的那么坏。内里独一容易疑惑的是 iterator_traits<RandomAccesslterator>::value_type的引用,那却正是STL的经典要领,当排序的时候,通过它来识别迭代器传过来的工具。假如我们想利用iterator_traits<RandomAccesslterator>::value_type,在它之前必需写typename,因为这是一个依赖于模板参数的范例名称,在这里,是RandomAccesslterator。假如想相识更多关于typename的信息,返回第七页) 在上面代码中,谁人引起贫苦的语句就是这行:
ElementType pivotValue(*i);
#p#分页标题#e#
因为它从被排序的范畴中拷贝了一个元素到当地的姑且工具中,在我们的接头中,这个元素是一个自动指针auto_ptr<Widget>,所以这个行动让被拷贝的谁人auto_ptr的值酿成了NULL,也就是在vector中的元素的值酿成了NULL,并且当竣事pivotValue的界说域时,它又自动删除了它指向的Widget。假如此时让sort函数直接返回,vector容器的值已经产生了变革,至少又一个Widget的值被释放了。由于快速排序是一个递归算法,每次递归城市选择pivot element,因此将会又好几个vector的元素被配置为NULL,同时会有好几个Widget工具被释放。
这是一个很是肮脏的陷阱,这也是为什么尺度委员会如此尽力的事情以制止你不会掉入这个陷阱中。为了尊重他们的事情,也为了你本身的好处,毫不要把auto_ptrs放入你的容器,甚至当你的STL平台答允你这么做,你也不要这么做。
假如你的方针是用一个包括smart pointer(智能指针)的容器,你照旧幸运的。包括smart pointers的容易都可以很好的事情。条款50,描写了在哪儿你能找到和STL容器团结十分完美的smart pointer。只是你不该该把auto_ptr放入容器,而不是smart pointer!