指针是 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. 英文构词法 —— circum- 前缀

    1. - circum-:表示环绕,周围,圆周: circle:圆:循环: circumference:圆周,周长,胸围: circumstance:环境: circumnavigation:环球航行 ...

  2. win7 UAC bypass(微软已经给予了三组组件绕过UAC启动的特权)

    fireworm同学的翻译: 原文在http://www.pretentiousname.com/misc/win7_uac_whitelist2.html我只翻译了其中关于原理的一小部分,有兴趣的可 ...

  3. yii2.0复选框默认选中

    <?php $model->node = array('0','2') ;?>   <? echo $form->field($model,'node')->che ...

  4. wpf控件开发基础(3) -属性系统(2)

    原文:wpf控件开发基础(3) -属性系统(2) 上篇说明了属性存在的一系列问题. 属性默认值,可以保证属性的有效性. 属性验证有效性,可以对输入的属性进行校验 属性强制回调, 即不管属性有无发生变化 ...

  5. Android4.0图库Gallery2代码分析(一) 程序整体结构

    Android4.0图库Gallery2代码分析(一) 程序整体结构 Gallery2的用例图分析:Gallery2主要功能是实现本地存储器.MTP存储器和网络存储器中媒体(图像和视频)的浏览.显示和 ...

  6. ASP.NET Page执行顺序(ASP.NET生命周期)

    此部分说明的生命周期只有部分: ---引用MSDN 阶段 说明 页请求 页请求发生在页生命周期开始之前.用户请求页时,ASP.NET 将确定是否需要分析和编译页(从而开始页的生命周期),或者是否可以在 ...

  7. Xcode 4.5( iOS6 SDK)、旧版本号cocos2d,支持iPhone5解析度

    支持iPhone5全屏 1假设没有支持iPhone5是否.直接运行程序可以准备提交.开放iPhone5模拟器,你会发现上面有黑色的程序.没有矩形. 2真正运行该程序时,.你会发现程序回程屏幕高度.它是 ...

  8. Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我说几句不相干的废话. 不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多 ...

  9. Win10忘记ubuntu子系统密码

    原文:Win10忘记ubuntu子系统密码 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/wf19930209/article/details/80 ...

  10. 编程算法 - 旋转矩阵 代码(C)

    旋转矩阵 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 输出旋转矩阵, 使矩阵是按对角线螺旋上升, 在输出规则确定以后, 就能够推断, 上升规律是, ...