http://blog.csdn.net/rogeryi/article/details/1442700

Part(1)
这篇文章试图说明如何使用auto_ptr和shared_ptr,从而使得动态分配对象的使用和管理更安全,方便。除了一般的使用说明外,更主要是说明它们之间的异同 —— 满足需求的不同和开销上的差异。
 
文章的多数知识都来源于:
 
1. Exceptional C++(Herb)Item 37 auto_ptr
2. Exceptional C++ Style(Herb)和C++ Coding Standard(Herb,Andrei)其中一些关于使用shared_ptr的论述
3. GC++和VC++ 8.0 auto_ptr的源代码
4. Boost库shared_ptr的源码和文档
 
auto_ptr和shared_ptr都是智能指针的一种实现,所谓智能指针,多数情况下都是指这样的一些对象:
 
1. 内部有一个动态分配对象的指针,拥有该对象的使用权和所有权(独占或共享)。
2. 重载*和->操作,行为上跟所拥有的对象的指针一致。
3. 当自身的生命期结束的时候,会做一些跟拥有对象相关的清理动作。
 
auto_ptr
 
auto_ptr是现在标准库里面一个轻量级的智能指针的实现,存在于头文件 memory中,之所以说它是轻量级,是因为它只有一个成员变量(拥有对象的指针),相关的调用开销也非常小。
 

下面的代码来自于VC++ 8.0里面的源码:

 
 
里面有个auto_ptr_ref的数据结构,我们可以把它忽略,这个只是内部使用的代理结构,用于一些隐式的const变化,我们客户端代码通常不会直接使用到它。
 
我们可以看到除了构造函数,拷贝构造函数,赋值函数,析构函数和两个重载操作符(*,->)外,还有get,release和reset三个函数,它们的作用分别是:
 
1. get,获得内部对象的指针
2. release,放弃内部对象的所有权,将内部指针置为空
3. reset,销毁内部对象并接受新的对象的所有权(如果使用缺省参数的话,也就是没有任何对象的所有权)
 

下面的例程来自Exceptional C++,Item 37:

 
// Example 2: Using an auto_ptr
//
void g()
{
    T* pt1 = new T;
    // right now, we own the allocated object
    // pass ownership to an auto_ptr
    auto_ptr<T> pt2( pt1 );
    // use the auto_ptr the same way
    // we'd use a simple pointer
    *pt2 = 12;       // same as "*pt1 = 12;"

    pt2->SomeFunc(); // same as "pt1->SomeFunc();"
    // use get() to see the pointer value
    assert( pt1 == pt2.get() );
    // use release() to take back ownership
    T* pt3 = pt2.release();
    // delete the object ourselves, since now
    // no auto_ptr owns it any more
     delete pt3;

} // pt2 doesn't own any pointer, and so won't
 // try to delete it... OK, no double delete
 

// Example 3: Using reset()
//
void h()
{
    auto_ptr<T> pt( new T(1) );
    pt.reset( new T(2) );
    // deletes the first T that was
    // allocated with "new T(1)"

} // finally, pt goes out of scope and
 // the second T is also deleted
 
 
从上面的例子来看,auto_ptr的使用很简单,通过构造函数拥有一个动态分配对象的所有权,然后就可以被当作对象指针来使用,当
auto_ptr对象被销毁的时候,它也会自动销毁自己拥有所有权的对象(嗯,标准的RAAI做法),release可以用来手动放弃所有权,reset
可用于手动销毁内部对象。
 
但实际上,auto_ptr是一个相当容易被误用并且在实际中常常被误用的类。原因是由于它的对象所有权占用的特性和它非平凡的拷贝行为。
 
auto_ptr的对象所有权是独占性的!
 
这决定了不可能有两个auto_ptr对象同时拥有同一动态对象的所有权,从而也导致了auto_ptr的拷贝行为是非对等的,其中伴随着对象所有权的转移。
 
我们仔细观察auto_ptr的源码就会发现拷贝构造和赋值操作符所接受的参数类型都是非const的引用类型(auto_ptr<_Ty>& ),而不是我们一般应该使用的const引用类型,查看源码我们会发现:
 
         auto_ptr(auto_ptr<_Ty>& _Right) _THROW0()
                 : _Myptr(_Right.release())

                 {        // construct by assuming pointer from _Right auto_ptr
                 }
 
         template<class _Other>
                 auto_ptr<_Ty>& operator=(auto_ptr<_Other>& _Right) _THROW0()

                 {        // assign compatible _Right (assume pointer)
                 reset(_Right.release());
                 return (*this);
                 }
 
拷贝过程中被拷贝的对象(_Right)都会被调用release来放弃所包括的动态对象的所有权,动态对象的所有权被转移了,新的
auto_ptr独占了动态对象的所有权。也就是说被拷贝对象在拷贝过程中会被修改,拷贝物与被拷贝物之间是非等价的。这意味着如下的代码是错误的(例程
来自 Exceptional C++ Item 37):
 
// Example 6: Never try to do work through

//            a non-owning auto_ptr
//
void f()
{
    auto_ptr<T> pt1( new T );
    auto_ptr<T> pt2;

    pt2 = pt1; // now pt2 owns the pointer, and

              // pt1 does not
    pt1->DoSomething();

              // error: following a null pointer
}
 
同时也不要将auto_ptr放进标准库的容器中,否则在标准库容器无准备的拷贝行为中(标准库容器需要的拷贝行为是等价的),会导致难以发觉的错误。(请参考Exceptional C++ Item 37获得更多信息)
 
auto_ptr特殊的拷贝行为使得使用它来远距离传递动态对象变成了一件十分危险的行为,在传递的过程中,一不小心就会留下一些实际为空但程序本身却缺少这样认知的auto_ptr对象。
 
简单的说来,auto_ptr适合用来管理生命周期比较短或者不会被远距离传递的动态对象,使用auto_ptr来管理动态分配对象,最好是局
限于某个函数内部或者是某个类的内部。也就是说,动态对象的产生,使用和销毁的全过程是处于一个小的受控的范围,而不会在其中加入一些适应未来时态的扩
展。
 
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Part(2)
shared_ptr

shared_ptr是Boost库所提供的一个智能指针的实现,正如其名字所蕴意的一样:
 

An important goal of shared_ptr is to provide a standard shared-ownership pointer.

shared_ptr的一个重要目的就是为了提供一个标准的共享所有权的智能指针。
                                                        —— Boost库文档
 
没错,shared_ptr就是为了解决auto_ptr在对象所有权上的局限性(auto_ptr是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针,当然这不会没有任何额外的代价……
 
首先一个shared_ptr对象除了包括一个所拥有对象的指针(px)外,还必须包括一个引用计数代理对象(shared_count)的指针(pn)。而这个引用计数代理对象包括一个真正的多态的引用计数对象(sp_counted_base)的指针(_pi),真正的引用计数对象在使用VC编译器的情况下包括一个虚表,一个虚表指针,和两个计数器。
 
下图中result是一个shared_ptr对象,我们可以清楚看到它展开后所包含的数据:
 

 
假设我们有多个(5个以上)shared_ptr共享一个动态对象,那么每个shared_ptr的开销比起只使用原生指针的开销大概在3,4 倍左右(这还是理想状况,忽略了动态分配带来的俑余开销)。如果只有一个shared_ptr独占动态对象,空间上开销更是高度十数倍!而 auto_ptr的开销只是使用原生指针的两倍。
 
时间上的开销主要在初始化和拷贝操作上,*和->操作符重载的开销跟auto_ptr是一样的。
 
当然开销并不是我们不使用shared_ptr的理由,永远不要进行不成熟的优化,直到性能分析器告诉你这一点,这是Hurb提出的明智的建 议。以上的说明只是为了让你了解强大的功能背后总是伴随着更多的开销,shared_ptr应该被使用,但是也不要过于滥用,特别是在一些 auto_ptr更擅长的地方。
 
下面是shared_ptr的类型定义:
 
 template<class T> class shared_ptr {
 

    public:
 

      typedef T element_type;
 

      shared_ptr(); // never throws

      template<class Y> explicit shared_ptr(Y * p);

      template<class Y, class D> shared_ptr(Y * p, D d);

      ~shared_ptr(); // never throws
 

      shared_ptr(shared_ptr const & r); // never throws

      template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws

      template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);

      template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
 

      shared_ptr & operator=(shared_ptr const & r); // never throws 

      template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r); // never throws

      template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
 

      void reset(); // never throws

      template<class Y> void reset(Y * p);

      template<class Y, class D> void reset(Y * p, D d);
 

      T & operator*() const; // never throws

      T * operator->() const; // never throws

      T * get() const; // never throws       bool unique() const; // never throws      long use_count() const; // never throws       operator unspecified-bool-type() const; // never throws       void swap(shared_ptr & b); // never throws };
 
大多数成员函数都跟auto_ptr类似,但是没有了release(请看注释),reset用来放弃所拥有对象的所有权或拥有对象的变更,会引起原有对象的引用计数的减少。
 
Note:
 
Boost文档里面的QA说明了为什么不提供release函数
 

Q. Why doesn't shared_ptr provide a release() function?

A. shared_ptr cannot give away ownership unless it's unique() because the other copy will still destroy
 the object.
Consider:
shared_ptr<int> a(new int);

shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2
 
int * p = a.release();
 

// Who owns p now? b will still call delete on it in its destructor.
Furthermore, the pointer returned by release() would be difficult to deallocate reliably,
as the source shared_ptr could have been created with a custom deleter.
 
use_count返回引用计数的个数,unique拥于确认是否是独占所有权(use_count为1),swap用于交换两个
shared_ptr对象(即交换所拥有的对象),有一个bool类型转换操作符使得shared_ptr可用于需要的bool类型的语境下,比如我们通
常用if(pointer)来判断某个指针是否为空。
 
Boost库里面有很多shared_ptr的使用例程,文档里面也列举了许许多多shared_ptr的用途,其中最有用也最常用的莫过于传
递动态分配对象,有了引用计数机制,我们现在可以安全地将动态分配的对象包裹在shared_ptr里面跨越模块,跨越线程的边界传递。
shared_ptr为我们自动管理对象的生命周期,嗯,C++也可以体会到Java里面使用引用的美妙之处了。
 

另外,还记得Effective C++里面(或者其它的C++书籍),Scott
Meyer告诉你的:在一个由多个模块组成的系统里面,一个模块不用试图自己去释放另外一个模块分配的资源,而应该遵循谁分配谁释放的原则。正确的原则但
是有时难免有时让人忽略(过于繁琐),将资源包装在shared_ptr里面传递,而shared_ptr保证了在资源不再被拥有的时候,产生资源的模块
的delete语句会被调用。

 
shared_ptr是可以拷贝和赋值的,拷贝行为也是等价的,并且可以被比较,这意味这它可被放入标准库的一般容器(vector,list)和关联容器中(map)。
 
shared_ptr可以用来容纳多态对象,比如所下面的例子:
 
class Base
{
}

class Derived : public Base
{
}
 

shared_ptr<Base> sp_base(new Derived);
 
 
甚至shared_ptr也具备多态的行为:
 
Derived* pd = new Derived;
 
shared_ptr<Derived> sp_derived(pd);
shared_ptr<Base> sp_base2(sp_derived);
 
上面的语句是合法的,shared_ptr会完成所需的类型转换,当shared_ptr的模版参数Base的确是Derived的基类的时候。
 
 
最后是一个小小的提醒,无论是使用auto_ptr还是shared_ptr,都永远不要写这样的代码:
 
A* pa = new A;
xxx_ptr<A> ptr_a_1(pa);
xxx_ptr<A> ptr_a_2(pa);
 
很明显,在ptr_a_1和ptr_a_2生命周期结束的时候都会去删除pa,pa被删除了两次,这肯定会引起你程序的崩溃,当然,这个误用的例子比较明显,但是在某种情况下,可能会一不小心就写出如下的代码(嗯,我承认我的确做过这样的事情):
 
void DoSomething(xxx_ptr<A>)
{

         //do something
}
 
class A
{
         doSomething()
         {

                 xxx_ptr<A> ptr_a(this);
                 DoSomething(ptr_a);
         }
};
 
int main()
{

         A a;
         a.doSomething();

         //continue do something with a, but it was already destory
}
 
在函数a.doSomething()里面发生了什么事情,它为了调用DoSomething所以不得不把自己包装成一个xxx_ptr,但是忘记在函数结束的时候,xxx ptr_a被销毁的同时也销毁了自己,程序或者立刻崩溃或者在下面的某个时间点上崩溃!
 
所以你在使用智能指针做为函数参数的时候请小心这样的误用,有时候使用智能指针作为函数参数不一定是一个好注意。比如请遵循下面的建议,请不要
在属于类型A的接口的一部分的非成员函数或者跟A有紧密联系的辅助函数里面使用xxx_ptr<A>作为函数的参数类型。

auto_ptr与shared_ptr ZZ的更多相关文章

  1. 初次窥见智能指针auto_ptr和shared_ptr

    #include <memory>//shared_ptr要用的头文件 using namespace std; class A //测试auto_ptr和shared_ptr的delet ...

  2. stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结

    stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结 1. auto_ptrauto_ptr主要是用来解决资源自动释放的问题,比如如下代码:voi ...

  3. auto_ptr, unique_ptr, shared_ptr and weak_ptr智能指针讲解

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解 ...

  4. auto_ptr和shared_ptr

    <Effective C++>在资源管理一节提到了智能指针,智能指针中最著名的当属auto_ptr和shared_ptr.本文主要研究两者的实现. auto_ptr的实现: templat ...

  5. C++ 之 auto_ptr and shared_ptr

    1.auto_ptr 这个所谓的只能指针有点鸡肋!  没有引用计数,而且还有一个所有权转移的情况! 当所有权转移后,以前的auto_ptr将会成为null 2.shared_ptr 增加了引用计数,没 ...

  6. C++ 智能指针 auto_ptr 和 shared_ptr

    首先,如果你不知道什么是智能指针,请先移步:C++智能指针简单剖析 1.auto_ptr #ifndef AUTO_PTR_H #define AUTO_PTR_H template<typen ...

  7. C++智能指针 auto_ptr、shared_ptr、weak_ptr和unique_ptr

    手写代码是理解C++的最好办法,以几个例子说明C++四个智能指针的用法,转载请注明出处. 一.auto_ptr auto_ptr这是C++98标准下的智能指针,现在常常已经被C++标准的其他智能指针取 ...

  8. 关于std:auto_ptr std:shared_ptr std:unique_ptr

    很多人听说过标准auto_ptr智能指针机制,但并不是每个人都天天使用它.这真是个遗憾,因为auto_ptr优雅地解决了C++设计和编码中常见的问题,正确地使用它可以生成健壮的代码.本文阐述了如何正确 ...

  9. <memory>(包括了auto_ptr,shared_ptr等各种指针)

    Memory elements This header defines general utilities to manage dynamic memory: Allocators allocator ...

随机推荐

  1. 连接Git@OSC操作步骤

    一.准备工作 软件下载 Git:地址 TortoiseGit:地址 二.安装与配置 1.Git安装 Git配置 设置客户端的用户名和email 然后,到Git@OSC 上面注册一个帐号. 这个帐号就是 ...

  2. (转)QEMU 使用的镜像文件:qcow2 与 raw

    原文:https://www.ibm.com/developerworks/cn/linux/1409_qiaoly_qemuimgages/ qcow2 的基本原理 qcow2 镜像格式是 QEMU ...

  3. 本地jar包 安装到本地仓库中的命令

    maven 项目 本地jar包 安装到本地仓库中去: 首先进入到该文件所在文件夹内 若不在直接绝对路径就可以.注意命令中的空格 mvn install:install-file  -Dfile=文件名 ...

  4. Java:对象的强、软、弱和虚引用的区别

    1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK ...

  5. 前后端完全分离开发模式Tomcat跨域问题处理

    公司新项目实现方案采用前后端完全分离架构,后端采用spring boot框架,前端纯HTML5开发部署会采用同一台服务器,但是在实现过程中分工开发出现ajax请求跨域问题故为解决开发问题发现如下解决方 ...

  6. Executors多线程

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...

  7. CentOS和RedHat等系列系统 yum源配置、时间同步

    一.yum源配置 1,进入yum源配置目录cd /etc/yum.repos.d 2,备份系统自带的yum源mv CentOS-Base.repo CentOS-Base.repo.bk下载163网易 ...

  8. Python——基本的方法

    格式化 我们经常会输出类似'亲爱的xxx你好!你xx月的话费是xx,余额是xx'之类的字符串,而xxx的内容都是根据变量变化的,所以,需要一种简便的格式化字符串的方式 >>> 'He ...

  9. 基于Spark GraphX计算二度关系

    关系计算问题描述 二度关系是指用户与用户通过关注者为桥梁发现到的关注者之间的关系.目前微博通过二度关系实现了潜在用户的推荐.用户的一度关系包含了关注.好友两种类型,二度关系则得到关注的关注.关注的好友 ...

  10. ROS:消息发布器和订阅器(c++)

    学习资料主要源自http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29 $ roscd beginner_t ...