auto_ptr到底能不能作为容器的元素

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

auto_ptr到底能不能作为容器的元素?

【摘要】对C++语言本身来说,它并不在乎用户把什么类型的对象作为STL容器的元素,因为模板类型参数在理论上可以为任何类型。比如说STL容器仅支持“值”语义而不支持“引用(&)”语义,并非因为模板类型参数不能为引用,而是因为如果容器元素为引用类型,就会出现“引用的引用”、“引用的指针”等C++语言不支持的语法和语义。智能指针是一种模拟原始指针行为的对象,因此理论上也可以作为容器的元素,就象原始指针可以作为容器元素一样。但是智能指针毕竟是一种特殊的对象,它们在原始指针共享实值对象的基础能力上增加了自动销毁实值对象的能力,如果将它作为容器的元素,可能导致容器之间共享元素对象实值,这不仅不符合STL容器的概念和“值”语义,也会存在安全隐患,同时也会存在许多应用上的限制,特别是象STL中的auto_ptr这样的智能指针。本文深入地阐述了auto_ptr这种较简单的智能指针“可以”或者“不可以”作为容器元素的根本原因,以及它作为容器元素会存在的限制和带来的问题,最后说明auto_ptr存在的真正意义、正确的使用方法以及它的替代品——带有引用计数能力的智能指针,当容器之间需要共享元素对象时,或者程序中存在大量的指针传递而担心资源泄漏时,这样的智能指针就特别有用。

【关键字】auto_ptr 容器智能指针

一、引言

Scott Meyers在《More Effective C++》[3]一书中对智能指针及其相关问题(构造、析构、复制、提领、测试以及类型转换等)作了深入的分析,其中也提到“STL的auto_ptr这种在复制时会把对实值对象的拥有权转交出去的智能指针不宜作为STL容器的元素”,而且在他的《Effective STL》[4]Item 8中明确指出了这一点。Nicolai M.Josuttis的《The C++ Standard Library》[5]中有一节专门针对auto_ptr的阐述也指出“auto_ptr不满足STL标准容器对元素的最基本要求”。但是他们都是从容器的需求、语义以及应用的安全性来阐述,而没有从语言的静态类型安全性和auto_ptr的实现方案角度深入地分析其原因,因此有些读者看了之后可能仍然不明就里:它是如何不满足容器需求的?它是如何违反C++的静态类型安全性从而避免误用的?

我们知道,可以作为STL容器的元素的数据类型一般来说需要满足下列条件:

(1)可默认构造的(Default Constructible),也即具有public的default constructor,不论是用户显式定义的还是编译器自动合成的。但是用户定义的带参数的constructor(包括copy constructor)会抑制编译器合成default constructor。实际上并非任何情况下任何一种容器都强制要求其元素类型满足这一要求,特别是关联式容器,因为只有序列式容器的某些成员函数才可能明确地或隐含地使用元素类型的默认构造函数,如果你不使用这样的成员函数,编译器就不需要元素类型的默认构造函数;

(2)可拷贝构造(Copy Constructible)和拷贝赋值(Copy Assignable)的,即具有public的copy constructor和copy assignment operator,不论是编译器自动合成的还是用户显式定义的。其它版本的operator=()重载并不会抑制编译器合成copy assignment operator,如果你没有显式定义它的话。这个条件可归结为:元素必须是可拷贝的(Copyable),但实际上拷贝赋值的要求也不是强制的,原

因和默认构造函数类似;

(3)具有public的destructor,不论是编译器自动合成的还是用户显式定义的;

(4)对于关联式容器,要求其元素必须是可比的(Comparable)。

auto_ptr满足上述条件吗?至少满足前三条,因此至少可以作为序列式容器的元素;如果为auto_ptr定义了比较运算符的话,应该还可以把它作为关联式容器的元素。

但是auto_ptr的特点是接管和转移拥有权,而不是像原始指针那样可以共享实值对象,即:auto_ptr在初始化时接管实值对象和拥有权,而在拷贝时(拷贝构造和拷贝赋值)会交出实值对象及其拥有权。因此,auto_ptr对象和它的拷贝绝对不会共享实值对象,任何两个auto_ptr也不应该共享同一个实值对象。这就是说,auto_ptr对象和它的拷贝并不相同。然而根据STL容器“值”语义的要求,可拷贝构造意味着一个对象必须和它的拷贝相同(标准中的正式定义比这稍复杂一些)。同样,可赋值意味着把一个对象赋值给另一个同类型对象将产生两个相同的对象。显然,auto_ptr不能满足这一要求,似乎与上面的结论矛盾!

那么问题究竟出在哪里呢?

二、copy constructor和copy assignment operator的形式

在揭开auto_ptr的神秘面纱之前需要了解copy constructor和copy assignment operator的几种合法形式。任何一个类都允许两种形式的copy constructor1(C代表任何一个类):

C(const C& copy);

C(C& copy);

同样,copy assignment operator也允许类似的两种形式(返回值类型视实际需要可改变):C& operator=(const C& copy);

C& operator=(C& copy);

实际上,由于copy assignment operator为普通的运算符重载成员函数,因此还可以为下列形式:C& operator=(C copy);

这两个函数具体是什么形式,取决于用户的定义或者该类的成员对象及其基类具有什么样的copy constructor和copy assignment operator。比如,如果基类的copy constructor为第一种形式,那么编译器自动为派生类合成的copy constructor也为第一种形式;相反为第二种形式。Copy assignment operator亦类似。具体细节可参考[8]。

这两种形式的区别就在于参数有无修饰符const:如果有const修饰,则该函数体不能修改实参对象(即拷贝源),也不能调用其non-const成员函数;如果没有const修饰,则该函数可以修改实参对象,也可以调用其non-const成员函数。

从语言的角度讲,任何对象都可以放到容器中(只要不是引用,因为STL容器不支持“引用”语义),只是某些类型的对象会存在安全隐患或者其容器会受到很大的应用限制。如果要防止用户把一些不适宜的对象放入容器中,就要求对象的设计和实现者使用一些语言支持的但不常用的特征。也就是说,要能够在编译阶段就阻止这种具有潜在危险性的行为。常用的方法就是迫使其违反C++静态类型安全规则。

1注意:C(C copy); 并非一个递归函数,它是非法的,因为它是一个悖论。

相关文档
最新文档