句柄类(
智能指针smart point)是存储指向动态分配(堆)
对象指针的类。
句柄类定义
一般定义
句柄类,除了能够在适当的时间自动删除指向的对象外,他们的工作机制很像C++的内置
指针。
智能指针在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象。他们也可以用于跟踪被多用户共享的动态分配对象。
在C++中一个通用的技术是定义包装(cover)类或句柄(handle)类,也称
智能指针。句柄类存储和管理
基类指针。
指针所指向对象的类型可以变化,它既可以指向
基类类型对象又可以指向派生类型对象。用户通过句柄类访问继承层次的操作。因为句柄类使用
指针执行操作,虚成员的行为将在运行时根据句柄实际绑定的对象类型而变化,即实现c++运行时
动态绑定。故句柄用户可以获得动态行为但无需操心
指针的管理。
理解实质
句柄类实际上通过复制指向引用计数器类型的
指针,来代替复制实际对象;从而使得复制对象变成复制
指针,实现虚拟复制(即是用复制对象的地址代替复制对象本身),以提高内存效率和访问速度。在涉及到大型、复杂对象以及一些不能复制或是不易复制的对象的复制控制问题时,显得特别有用。
涉及技术
引入使用计数
定义句柄类或
智能指针的通用技术是采用一个使用计数(use count)。句柄类将一个计数器与类指向的对象相关联。使用计数跟踪该类有多少个
指针共享同一对象。当使用计数为0时,就删除该类对象,否则再删除类对象时,只要
引用计数不为0,就不删除实际的类对象,而是是引用计数减1,实现虚删除。
使用计数类
为了便于理解,我们定义一个实际类(Point),一个引用计数器类(UPoint),一个句柄类(Handle),后面将有例子给以参考。
实现使用计数有两种经典策略:一种是定义一个单独的具体的类用以
封装使用计数和指向实际类的
指针;
另一种是定义一个单独的具体的类用以封装
引用计数和类的对象成员。我们称这种类为计数器类(UPoint)。在计数器类中,所有成员均设置为private,避免外部访问,但是将句柄类Handle类声明为自己的
友元,从而使句柄类能操纵引用计数器。
写时复制
写时复制(copy on write)技术是解决如何保证要改动的那个引用计数器类UPoint对象不能同时被任何其他的句柄类(Handle类)所引用。通俗的来说,就是当实际对象Point被多个Handle类的
指针共享时,如果需要通过指针改变实际对象Point,而其他的指针又需要保持原来的值时,这就有矛盾了。打个不恰当的比方来说,两个以上的人共有5W块钱,如果其中一个人想用这5W块钱去消费,那就必须通知其他人。否则在这个人消费了5块钱后,其他人还以为他们仍然有5W块钱,如果这儿时候,他们去买5W的东西,就会发现钱变少了或是没有了,此时他们就陷入债务的泥团。在C++中通过
指针访问已经删除或是不存在的对象,将是非常危险的。有可能系统提示该行为未定义,也有可以内存非法访问,还有可能使系统崩溃。
句柄类的实例
完成源码程序
运行结果分析
handle::h 23 34
handle::tmp 23 34 //h和tmp句柄类对象实际共享同一Point(23,34)对象;
handle::val 0 0 //val默认指向实际对象Point(0,0);
~UPoint! //由于写时复制时:up = new UPoint(up->p);创建了一个临时UPoint
~Point! //对象;调用完后释放,由于Point是UPoint的成员对象,所以先
//UPoint,然后是Point。
handle::h 100 34 //通过修改赋值val = h.x(100);后,h和val共享同一对象Point(100,34)
handle::tmp 23 34
handle::val 100 34
//依次释放内存;
~Handle! //val句柄类对象;val(100,34)
~UPoint!
~Point!
~Handle! //tmp句柄类;tmp(23,34)
~UPoint!
~Point!
~Handle! //只释放了一个句柄类Handle的
指针,没有实际对象;
请按任意键继续. . .
相关的类
代理(surrogate)类,又称委托。(后续完善)
这篇关于句柄类的介绍,在理论部分还是挺不错的,但举的例子就不敢恭维了,UPoint类的目的是从Handle类中将“
引用计数”功能分离出来单独实现,让Handle类专注于实现“
内存管理”功能,因此UPoint类必须能够实现自动计数,而不应该在Handle类中再来对其加减。
上面分析结果的最后一段:
~Handle! //val句柄类对象;val(100,34) ~UPoint! ~Point! ~Handle! //tmp句柄类;tmp(23,34) ~UPoint! ~Point! ~Handle! //只释放了一个句柄类Handle的指针,没有实际对象; 请按
任意键继续. . .
分析有误,改为:
~Handle! //val句柄类对象;val(100,34)
~UPoint! //temp指向的UPoint对象
~Point! //temp指向的UPoint对象的内嵌Point对象
~Handle! //tmp句柄类;tmp(23,34)
~UPoint! //val和h共同指向的Upoint对象
~Point! //val和h共同指向的Upoint对象的内嵌对象Point
~Handle! //释放h句柄类
句柄类例子