指针是 C 与其他语言区别的重要特征之一,在 C++ 中,指针也被广泛运用,我们通过指针实现多态。然而,众所周知,指针的使用必须小心,否则很容易造成内存泄漏 Memory Leak。当我们有几个指针指向同一个对象时有其应该注意,关于何时释放这个对象:
(1) 如果释放的太早,那么其它的指针仍然指向这片内存,如果再使用它会造成未定义行为。
(2) 如果一直不释放可能会丢失最后一个指向这个对象的指针 导致内存无法被释放。

用 C++ 的方法来解决这种问题就是建立一个类来包含需要管理的指针 ,由于这些类往往与被管理者相绑定 ,所以它们被称为 handel 类 ,人们再建立这种 handel 类的同时一般保留了它包含的指针的种种特性,所以也称这种类为 智能指针 smart pointer。

最简单的 handel

这种 handel 只是一个包含了对象指针的容器,当对象的指针绑定到 handel 上后 ,就不需要手动delete 对象 ,handel 类负责对象的析构(在 handel 离开作用域时)。stl 中 的 auto_ptr 就是这种例子。

下面给出一个简单的 AutoPtr 实现:

/*
* File : auto_prt.h
* Discription : 智能指针 
* 指针存储的最简单策略 , 将指针存入对象中当对象被析构指针自动被delete
* AutoPtr 和 指针是 一对一的关系
* Usage : AutoPtr< ClassType > ap_type = AutoPtr< ClassType >( new ClassType() );
* ap_type->method();
*/

#ifndef _PATTERN_AUTOPTR_H
#define _PATTERN_AUTOPTR_H

#include "../common/common.h"

namespace c_toto
{

template<class T> class AutoPtr
{
public:
  AutoPtr( AutoPtr<T> & ap ): ptr( ap.ptr ) {   ap.ptr = NULL; }

AutoPtr<T> & operator=( AutoPtr<T> & ap )
{
if( ptr )

delete ptr;
ptr = NULL;
}
ptr = ap.ptr;
ap.ptr = NULL;
}

public:
  AutoPtr( T * p = NULL ) : ptr( p ) {}

~AutoPtr() { delete ptr; }

bool Valid()
{
if( ptr )return true;
return false;
}

T & operator*() { return *ptr; }

const T & operator*() const { return *ptr; }

T * operator->() { return ptr; }
  const T * operator->() const  { return ptr; }
private:
T * ptr;
};

}; // namespace c_toto

#endif // #ifndef _PATTERN_AUTOPTR_H

需要注意的是,由于 AutoPtr 和指针是一对一的关系,那么 AutoPtr 类中的赋值操作符和拷贝构造函数必须保证只有一个 AutoPtr 指向对应的指针,在这里我们的策略是:

AutoPtr( AutoPtr<T> & ap ) 中的参数 AutoPtr ap 作废 ,构造的新 AutoPtr 接管原 AutoPtr 的指针。

AutoPtr<T> & operator=( AutoPtr<T> & ap ) 中的= 左值如果有指针 ,则 delete 掉原指针,接管右值的指针 ,右值作废。

这种简单的 AutoPtr 可以用于异常处理。当我们的函数执行中抛出异常,在异常前分配的资源需要在 catch 中手动释放,这样往往会有遗漏.
如果我们把分配的资源(往往是指针)存放在 AutoPtr 中,那么资源在超出它们的作用于时会自动释放,AutoPtr 会自动调用它们各自的析构函数。

引用计数句柄

这种句柄的目的是实现句柄和对象的多对一关系(对应于指针的情形就是多个指针指向同一对象),这样我们就可以按照常规定以来通过复制句柄来复制对象的指针。为了保证对象能够被释放,我们的句柄必须知道同时有多少个其他的句柄正指向当前的对象,所以我们引入引用计数策略。

(1) 这个引用计数功能不能放在句柄中. 因为如果句柄被复制,它的引用信息也会被复制,那么绑定了同一指针的句柄们的引用信息就无法统一管理。

(2) 把引用计数放在对象中也不合适,这需要我们改写现有类。我们可以建立一个中间类用来包含引用计数功能和需要用句柄绑定的指针。

可以看到,这个中间类和我们的指针是一对一关系 ,和我们的句柄是一对多关系。

现在然我们看看如何实现 Handel 中的基本操作:

(1) 默认构造函数

由于句柄现在面对的只是我们添加的中间类,所以只需简单的调用中间类的默认构造即可。在中间类的默认构造函数中我们将指针清零,引用置一。

(2) 拷贝构造函数

拷贝是对于句柄而言,我们通过将引用计数自加来避免对指针所值的内容拷贝。

(3) 赋值操作符

句柄间进行赋值操作时,=左边的句柄所指内容会被改写,所以需先让它的引用--(当引用为一时注意delete), 然后在++等号右边的句柄引用 。
用一个 handel 对 handel 自身的赋值是无意义的行为。

(4) 析构函数

每次析构时检查引用计数是否为一,如果是,说明当前句柄是最后一个保存这个指针的句柄,在析构函数中需要 delete 。
     实际上我们可以将引用计数功能抽象成一个类,直接由句柄管理,这样就可以去掉中间层,减少程序复杂度。

/*
* File : sharedptr
* Discription : 加入了引用记数的指针存储策略 
*/

#ifndef _PATTERNS_SHAREDPTR_H_
#define _PATTERNS_SHAREDPTR_H_

#include "../common/common.h"

namespace c_toto
{

template<class T>
class SharedPtr;

class Reference
{
public:
  Reference() : ref_count( new int(1) ) {  }
  Reference( const Reference & r ) : ref_count( r.ref_count ) { (*ref_count)++; }

~Reference()
{
(*ref_count)--;
if( (*ref_count) == 0 )
delete ref_count; 
}

bool Only() { return ( *ref_count == 1 ); }

bool Rebind( const Reference & r )
{
(*ref_count)--;
(*r.ref_count)++;

if( *ref_count == 0 )
{
delete ref_count;
ref_count = r.ref_count;
return true;
}
ref_count = r.ref_count;
return false;
}

private:
   Reference & operator=( const Reference & r_ );

int * ref_count;
};

////////////////////////////////////////////////////////////////////////////////

template<class T>
class SharedPtr
{
public:
  SharedPtr( T * p = NULL ): ptr( p ) {}

~SharedPtr() { if( ref.Only() ) { delete ptr; } }

SharedPtr<T> & operator=( const SharedPtr<T> & sp )
{
if( ref.Rebind( sp.ref ) )
{
delete ptr; 

ptr = sp.ptr;
return *this;  
}

private:
Reference ref;
T * ptr;
}; // class

}; // namespace c_toto

#endif // #ifndef _PATTERNS_SHAREDPTR_H_

http://www.cnblogs.com/yc_sunniwell/archive/2010/07/12/1775619.html

C++中代理类和句柄类的更多相关文章

  1. c++ 容器、继承层次、句柄类

    一.容器与继承 在容器中保存有继承关系的对象,如果定义成保存基类对象,则派生类将被切割,如果定义成保存派生类对象,则保存基类对象又成问题(基类对象将被强制转换成派生类对象,而派生类中定义的成员未被初始 ...

  2. C++中的句柄类

    初次在<C++ Primer>看到句柄,不是特别理解.在搜索相关资料后,终于有了点头绪. 首先明白句柄要解决什么问题.参考文章<C++ 沉思录>阅读笔记——代理类 场景: 我们 ...

  3. Java中动态代理技术生成的类与原始类的区别 (转)

    用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后 ...

  4. Java中动态代理技术生成的类与原始类的区别

    用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后 ...

  5. c++中代理类的学习

    https://blog.csdn.net/lcg910978041/article/details/51468680 C++代理类是为了解决这样的问题: 容器通常只能包含一种类型的对象,所以很难在容 ...

  6. Java中动态代理技术生成的类与原始类的区别 (good)

    用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后 ...

  7. C++的句柄类

    上一篇文件介绍了关于C++代理类的使用场景和实现方法,但是代理类存在一定的缺陷,就是每个代理类会创建一个新的对象,无法避免一些不必要的内存拷贝,本篇文章引入句柄类,在保持代理类多态性的同时,还可以避免 ...

  8. code of C/C++(3) - 从 《Accelerated C++》源码学习句柄类

    0  C++中多态的概念 多态是指通过基类的指针或者引用,利用虚函数机制,在运行时确定对象的类型,并且确定程序的编程策略,这是OOP思想的核心之一.多态使得一个对象具有多个对象的属性.class Co ...

  9. 深度模拟java动态代理实现机制系类之三

    这里的内容就比较复杂了,要实现的是对任意的接口,对任意指定的方法,以及对任意指定的代理类型进行代理,就更真实的模拟出java虚拟机的动态代理机制 罗列一下这里涉及的类.接口之间的关系,方便大家学习.1 ...

随机推荐

  1. Linux初接触随笔02

    刚开始把Linux既拿来用,又拿来学习怎么用,感觉真不顺手,手上应该有一本实体书籍,专门拿来学习用会好点,但是我现在手头没有,又把系统作为日常使用,只能说太不友好了,坚持不住了,以后等需要用的时候再弄 ...

  2. html中DIV+CSS与TABLE布局方式的区别及HTML5新加入的结构标签(转)

    DIV与TABLE布局的区别 div 和 table 的加载方式不同,div 的加载方式是即读即加载,遇到 <div> 没有遇到 </div> 的时候一样加载 div 中的内容 ...

  3. hadoop 3.x 启动过程中 Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).

    出现这种状况是因为当前账号没有配置ssh免密登录 进入到以下目录,查看是否生成过秘钥对,如果有的话直接ssh-copy-id 主机名 没有的话执行ssh-keygen -t rsa后再重新执行ssh- ...

  4. 配置ANDROID_HOME

    原文:配置ANDROID_HOME 1.在环境变量中设置一个名为ANDROID_HOME,变量值为SDK路径 2.添加至Path中 备注:ANDROID_HOME的变量值仅允许一个

  5. Android高度仿新浪微博引导页面

    在写这一篇文章之前,先向大家推荐一篇博文:http://blog.csdn.net/dawanganban/article/details/17297671 感谢这位博主,我在该博主的基础上完成了对新 ...

  6. On-demand diverse path computation for limited visibility computer networks

    In one embodiment, a source device detects a packet flow that meets criteria for multi-path forwardi ...

  7. lzugis——Arcgis Server for JavaScript API在自己的定义InfoWindow

    你看到这个标题嫌烦.因为我最近一直与研究问题,相关文章使这些也可以只,同时要读我文章的朋友.我的文章能够给你带来帮助. 在相关的内部的前两篇文章,达到InfoWindow经div实现的东西,成Info ...

  8. NP、NP-完全、NP-难问题

    What are the differences between NP, NP-Complete and NP-Hard? 0. 基本定义 判定问题(decision problem):一个答案是是或 ...

  9. WPF 控制程序只能启动一次

    原文:WPF 控制程序只能启动一次 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/jsyhello/article/details/7411898 ...

  10. Information centric network (icn) node based on switch and network process using the node

    The present invention relates to an apparatus for supporting information centric networking. An info ...