C++中代理类和句柄类
指针是 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++中代理类和句柄类的更多相关文章
- c++ 容器、继承层次、句柄类
一.容器与继承 在容器中保存有继承关系的对象,如果定义成保存基类对象,则派生类将被切割,如果定义成保存派生类对象,则保存基类对象又成问题(基类对象将被强制转换成派生类对象,而派生类中定义的成员未被初始 ...
- C++中的句柄类
初次在<C++ Primer>看到句柄,不是特别理解.在搜索相关资料后,终于有了点头绪. 首先明白句柄要解决什么问题.参考文章<C++ 沉思录>阅读笔记——代理类 场景: 我们 ...
- Java中动态代理技术生成的类与原始类的区别 (转)
用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后 ...
- Java中动态代理技术生成的类与原始类的区别
用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后 ...
- c++中代理类的学习
https://blog.csdn.net/lcg910978041/article/details/51468680 C++代理类是为了解决这样的问题: 容器通常只能包含一种类型的对象,所以很难在容 ...
- Java中动态代理技术生成的类与原始类的区别 (good)
用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑. 平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后 ...
- C++的句柄类
上一篇文件介绍了关于C++代理类的使用场景和实现方法,但是代理类存在一定的缺陷,就是每个代理类会创建一个新的对象,无法避免一些不必要的内存拷贝,本篇文章引入句柄类,在保持代理类多态性的同时,还可以避免 ...
- code of C/C++(3) - 从 《Accelerated C++》源码学习句柄类
0 C++中多态的概念 多态是指通过基类的指针或者引用,利用虚函数机制,在运行时确定对象的类型,并且确定程序的编程策略,这是OOP思想的核心之一.多态使得一个对象具有多个对象的属性.class Co ...
- 深度模拟java动态代理实现机制系类之三
这里的内容就比较复杂了,要实现的是对任意的接口,对任意指定的方法,以及对任意指定的代理类型进行代理,就更真实的模拟出java虚拟机的动态代理机制 罗列一下这里涉及的类.接口之间的关系,方便大家学习.1 ...
随机推荐
- 我为什么要写FansUnion个人官网-BriefCMS-电子商务malling等系统
不少朋友一直关注我最近几个月,已经做的和正在做的项目,比如个人官网.BriefCMS.电子上午malling等系统. 但是呢,部分朋友比较好奇,为啥要去写.他们比较疑惑的是,市面上已经有很多类似的系统 ...
- 【BZOJ 1036】[ZJOI2008]树的统计Count
[题目链接]:http://www.lydsy.com/JudgeOnline/problem.php?id=1036 [题意] [题解] 树链剖分入门题; 每一条链维护一个线段树就好; uppest ...
- 微信小程序预览图片
选择图片时可设置图片是否是原图,图片来源.这用的也挺常见的,比如个人中心中设置头像,可以与wx.upLoadFile()API使用 主要方法: wx.chooseImage(object) wxml ...
- 一种基于uCos-II操作系统和lwIP协议栈的IEEE-1588主站以及基于该主站的报文处理方法
主站以及应用于电力系统的支持IEEE‐1588协议的主时钟(IEEE‐1588主站)的实现方法.该方法是在一个低成本的硬件平台上,借助uCos‐II操作系统和TCP/IP的协议栈,对以太网数据进行了分 ...
- 于CentOS 6.5编译器安装Git 1.8
yum install git版本号是太老,它是1.7.1. 在这里,我们将介绍如何编译和安装Git 1.8 yum install curl-devel expat-devel gettext-de ...
- PAT 1031-1040 题解
早期部分代码用 Java 实现.由于 PAT 虽然支持各种语言,但只有 C/C++标程来限定时间,许多题目用 Java 读入数据就已经超时,后来转投 C/C++.浏览全部代码:请戳 本文谨代表个人思路 ...
- VS编译环境中TBB配置和C++中lambda表达式
TBB(Thread Building Blocks),线程构建模块,是由Intel公司开发的并行编程开发工具,提供了对Windows,Linux和OSX平台的支持. TBB for Windows ...
- USER_AGENT 知识
USER-AGENT 是 Http 协议中的一部分,属于头域的组成部分,User Agent也简称 UA,意为用户代理,当用户通过浏览器发送 http 请求时,USER_AGENT 起到表明自己身份的 ...
- c语言指针详细解释
指针是C语言中广泛使用的一种数据类型. 运用指针编程是C语言最基本的风格之中的一个.利用指针变量能够表示各种数据结构: 能非常方便地使用数组和字符串: 并能象汇编语言一样处理内存地址,从而编出精练而高 ...
- mysqldump 定时备份数据(全量)
MYSQL 数据库备份有很多种(cp.tar.lvm2.mysqldump.xtarbackup)等等,具体使用哪一个还要看你的数据规模.下面给出一个表 #摘自<学会用各种姿态备份Mysql数据 ...