C++智能指针文档格式.docx
- 文档编号:22538123
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:25
- 大小:26.08KB
C++智能指针文档格式.docx
《C++智能指针文档格式.docx》由会员分享,可在线阅读,更多相关《C++智能指针文档格式.docx(25页珍藏版)》请在冰豆网上搜索。
_ptr(obj._ptr),_val(obj._val)
++_ptr->
use;
HasPtr&
operator=(constHasPtr&
);
~HasPtr()
if(--_ptr->
use==0)
delete_ptr;
private:
U_Ptr*_ptr;
int_val;
接受一个指针和一个int值的HasPtr构造函数使用其指针形参创建一个新的U_Ptr对象。
HasPtr构造函数执行完毕后,HasPtr对象指向一个新分配的U_Ptr对象,该U_Ptr对象存储给定指针。
新U_Ptr中的使用计数为1,表示只有一个HasPtr对象指向它。
复制构造函数从形参复制成员并增加使用计数的值。
复制构造函数执行完毕后,新创建对象与原有对象指向同一U_Ptr对象,该U_Ptr对象的使用计数加1。
析构函数将检查U_Ptr基础对象的使用计数。
如果使用计数为0,则这是最后一个指向该U_Ptr对象的HasPtr对象,在这种情况下,HasPtr析构函数删除其U_Ptr指针。
删除该指针将引起对U_Ptr析构函数的调用,U_Ptr析构函数删除int基础对象。
赋值与引用计数
首先将右操作数中的使用计数加1,然后将左操作数对象的使用计数减1并检查这个使用计数。
像析构函数中那样,如果这是指向U_Ptr对象的最后一个对象,就删除该对象,这会依次撤销int基础对象。
将左操作数中的当前值减1(可能撤销该对象)之后,再将指针从rhs复制到这个对象。
赋值照常返回对这个对象的引用。
HasPtr&
HasPtr:
:
operator=(constHasPtr&
rhs)
++rhs.ptr->
//incrementusecountonrhsfirst
if(--ptr->
deleteptr;
//ifusecountgoesto0onthisobject,deleteit
ptr=rhs.ptr;
//copytheU_Ptrobject
val=rhs.val;
//copytheintmember
return*this;
}
这个赋值操作符在减少左操作数的使用计数之前使rhs的使用计数加1,从而防止自身赋值。
如果左右操作数相同,赋值操作符的效果将是U_Ptr基础对象的使用计数加1之后立即减1。
值型类
复制值型对象时,会得到一个不同的新副本。
对副本所做的改变不会反映在原有对象上,反之亦然。
string类是值型类的一个例子。
要使指针成员表现得像一个值,复制HasPtr对象时必须复制指针所指向的对象:
复制构造函数不再复制指针,它将分配一个新的int对象,并初始化该对象以保存与被复制对象相同的值。
每个对象都保存属于自己的int值的不同副本。
因为每个对象保存自己的副本,所以析构函数将无条件删除指针。
赋值操作符不需要分配新对象,它只是必须记得给其指针所指向的对象赋新值,而不是给指针本身赋值。
//复制构造函数定义
HasPtr(constHasPtr&
orig):
ptr(newint(*orig.ptr)),val(orig.val){}
//赋值函数定义
*ptr=*rhs.ptr;
//copythevaluepointedto
//copytheint
策略2:
句柄类
C++中一个通用的技术是定义包装(cover)类或句柄类。
句柄类存储和管理基类指针。
指针所指对象的类型可以变化,它既可以指向基类类型对象又可以指向派生类型对象。
用户通过句柄类访问继承层次的操作。
因为句柄类使用指针执行操作,虚成员的行为将在运行时根据句柄实际绑定的对象的类型而变化。
因此,句柄的用户可以获得动态行为但无须操心指针的管理。
包装了继承层次的句柄有两个重要的设计考虑因素:
*像对任何保存指针的类一样,必须确定对复制控制做些什么。
包装了集成层次的句柄通常表现得像一个智能指针或者像一个值。
*句柄类决定句柄接口屏蔽还是不屏蔽继承层次,如果不屏蔽继承层次,用户必须了解和使用基本层次中的对象。
智能指针就是模拟指针动作的类。
所有的智能指针都会重载->
和*操作符。
classSmart_Pointer
//defaultconstructor:
unboundhandle
Smart_Pointer():
_p(0),_use(newstd:
size_t
(1)){}
//attachesahandletoacopyoftheBaseobject
Smart_Pointer(constBase&
//copycontrolmemberstomanagetheusecountandpointers
Smart_Pointer(constSmart_Pointer&
i):
_p(i._p),_use(i._use){++*use;
~Smart_Pointer(){decr_use();
Smart_Pointer&
operator=(constSmart_Pointer&
//memberaccessoperators
constBase*operator->
()const
if(_p)
return_p;
else
throwstd:
logic_error("
unboundBase"
constBase&
operator*()const
return*p;
Base*_p;
std:
size_t*_use;
voiddecr_use()
if(--*use==0)
delete_p;
delete_use;
OpenCV的Ptr模板类
OpenCV中的智能指针Ptr模板类就是采用分离引用计数型的句柄类实现技术。
以OpenCV的人脸识别为例,实现了人脸识别中的三种算法:
Eigenface、FisherFace和基于LBP特征的算法。
这三种算法也分别封装成三个类:
Eigenfaces、Fisherfaces、LBPH类,这三个类均派生自FaceRecognizer类,而FaceRecognizer类则派生自Algorithm类。
FaceRecognizer类是一个抽象基类。
OpenCV就是采用一个泛型句柄类Ptr管理FaceRecognizer类的对象。
template<
typename_Tp>
classCV_EXPORTSPtr
public:
//!
emptyconstructor
Ptr();
takeownershipofthepointer.Theassociatedreferencecounterisallocatedandsetto1
Ptr(_Tp*_obj);
callsrelease()
~Ptr();
copyconstructor.Copiesthemembersandcallsaddref()
Ptr(constPtr&
ptr);
template<
typename_Tp2>
Ptr(constPtr<
_Tp2>
&
copyoperator.Callsptr.addref()andrelease()beforecopyingthemembers
Ptr&
operator=(constPtr&
incrementsthereferencecounter
voidaddref();
decrementsthereferencecounter.Ifitreaches0,delete_obj()iscalled
voidrelease();
deletestheobject.Overrideifneeded
voiddelete_obj();
returnstrueiffobj==NULL
boolempty()const;
castpointertoanothertype
Ptr<
ptr();
constPtr<
ptr()const;
helperoperatorsmaking"
Ptr<
T>
ptr"
useverysimilarto"
T*ptr"
.
_Tp*operator->
();
const_Tp*operator->
()const;
operator_Tp*();
operatorconst_Tp*()const;
_Tp*obj;
//<
theobjectpointer.
int*refcount;
theassociatedreferencecounter
当创建一个FaceRecognizer的派生类Eigenfaces的对象时,我们把这个Eigenfaces对象放进Ptr对象内,就可以依赖句柄类Ptr确保Eigenfaces对象自动被释放。
FaceRecognizer>
model=createEigenFaceRecognizer(num_components,threshold);
当利用createEigenFaceRecognizer动态创建一个Eigenfaces的对象后,立即把它放进Ptr<
FaceRecognizer>
中进行管理。
获得资源后立即放进管理对象,管理对象运用析构函数确保资源被释放。
createEigenFaceRecognizer(intnum_components,doublethreshold)
returnnewEigenfaces(num_components,threshold);
我们注意到在createEigenFaceRecognizer实现源码中,返回了动态地创建Eigenfaces对象,并且隐式的转换成Ptr。
由C++的泛型句柄类思考OpenCV的Ptr模板类
时间
2013-03-2422:
44:
00
博客园-原创精华区
原文
主题
OpenCV
C++
泛型
OpenCV(计算机视觉库)2.4.4版本已经发布了,OpenCV发展到现在,由最初的C接口变成现在的C++接口,让开发者写程序越来越简单,接口越来越合理,也不用担心内存释放问题。
但要理解内部的一些实现机制,还真要费点功夫,这对开发者的C++基础要求越来越高。
本文就是笔者在做项目过程中的一点感悟,由C++泛型句柄类思考OpenCV的Ptr模板类的实现。
1、C++泛型句柄类
我们知道在包含指针成员的类中,需要特别注意类的复制控制,因为复制指针时只复制指针中的地址,而不会复制指针指向的对象。
这将导致当两个指针同时指向同一对象时,很可能一个指针删除了一对象,另一指针的用户还认为基础对象仍然存在,此时就出现了悬垂指针。
当类中有指针成员时,一般有两种方式来管理指针成员:
一是采用值型的方式管理
,每个类对象都保留一份指针指向的对象的拷贝;
另一种更好的方式是
使用智能指针
,从而实现指针指向的对象的共享。
(可参看《C++Primer第四版》P419)
智能指针(smartpointer)
的一种通用实现技术是使用
引用计数(referencecount)。
智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。
每次创建类的新对象时,初始化指针并将引用计数置为1;
当对象作为另一对象的父本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;
调用析构函数时,析构函数减少引用计数(如果引用计数减至0,则删除基础对象)。
智能指针实现引用计数有两种经典策略:
一是引入
辅助类(包含引用计数型)
,二是使用
句柄类(分离引用计数型)
。
∙辅助类的解决方案是,定义一个单独的具体类来封装指针和相应的引用计数。
可参考我之前写的一个博客:
辅助类实现智能指针代码如下,参考《C++沉思录》,利用UPoint类作为辅助类封装了指针Point*和引用计数,从而代替指针Point*。
这个技术的主要思想是当多个Handle类的对象在堆上共享同一个Point*指向的内存区时,我们在这个内存区上多分配一点空间存放引用计数,那么我们就可以知道有多少个Handle类的对象在共享Point*指向的内存区,当引用计数为0时,我们就可以很放心的释放掉这块内存区,而不会出现悬垂指针了。
1//辅助类UPoint
2classUPoint{
3private:
4friendclassHandle;
5intu;
6Pointp;
7UPoint(constPoint&
pp):
u
(1),p(pp)
8{
9
10}
11UPoint(intxx,intyy):
p(xx,yy),u
(1)
12{
13
14}
15UPoint():
u
(1)
16{
17
18}
19};
20
21classHandle{
22public:
23Handle():
up(newUPoint)
24{
25
26}
27Handle(intx,inty):
up(newUPoint(x,y))
28{
29
30}
31Handle(constPoint&
up):
up(newUPoint(up))
32{
33
34}
35//复制构造函数
36Handle(constHandle&
other):
up(other.up)
37{
38++up->
u;
39}
40//赋值操作符
41Handle&
operator=(constHandle&
other)
42{
43++other.up->
44if(--up->
u==0){
45deleteup;
46}
47up=other.up;
48return*this;
49}
50~Handle()
51{
52if(--up->
u==0){
53deleteup;
54}
55}
56private:
57UPoint*up;
58};
基于辅助类的智能指针实现方式需要创建一个依赖于Point类的新类,这样做对于特定的类而言是很好,但却让我们很难将句柄绑定到Point类派生的新类对象上。
因此,就有了分离引用计数型的句柄类实现了。
可参看《C++沉思录》P69页,OpenCV中的智能指针模板类Ptr就是基于这种计数实现。
下面是采用模板的方式实现的一个泛型句柄类(分离引用计数型),参考《C++Primer第四版》P561。
从下面可以看出辅助类消失了,
在这个句柄类中,我们用指向类型T的指针(共享对象,类似于上面的Point类型)和指向一个int的指针表示引用计数。
使用T*很重要,因为正是T*使我们不仅能够将一个Handle绑定到一个T类型的对象,还能将其绑定到一个继承自T的类的对象。
这个类模板的数据成员有两个:
指向某个实际需要管理的类型的数据的指针以及它的引用计数。
它定义了复制构造函数、赋值操作符以及解引、成员访问操作符。
其中解引操作符返回的是实际需要管理的数据,而箭头操作符返回的是这个指针。
这两个操作符使得我们操作Handle<
的对象就跟操作T的对象一样。
1#ifndefHANDLE_H
2#defineHANDLE_H
3
4template<
classT>
classHandle
5{
6public:
7//构造函数:
空指针
8Handle(T*p=0):
ptr(p),use(newsize_t
(1)){}
9//重载解引和箭头操作符
10T&
operator*();
11T*operator->
();
12constT&
operator*()const;
13constT*operator->
()const;
14//复制构造函数
15Handle(constHandle&
h):
ptr(h.ptr),use(h.use){++*use;
16//重载赋值操作符
17Handle&
18//析构函数
19~Handle(){rem_ref();
20private:
21//共享的对象
22T*ptr;
23//引用计数
24size_t*use;
25//删除指针的具体函数
26voidrem_ref()
27{
28if(--*use==0)
29{
30deleteptr;
31deleteuse;
32}
33}
34};
35
36template<
37inlineHandle<
Handle<
operator=(constHandle&
38{
39//右操作数引用计数+1
40++*rhs.use;
41//删除左操作数
42rem_ref();
43//具体对象的赋值
44ptr=rhs.ptr;
45use=rhs.use;
46return*this;
47}
48
49template<
inlineT&
operator*()
50{
51if(ptr)
52return*ptr;
53//空指针时抛出异常
54throwstd:
runtime_error("
dereferenceofunboundHandle"
55}
56
57template<
inlineT*Handle<
operator->
()
58{
59if(ptr)
60returnptr;
61//空指针时抛出异常
62throwstd:
accessthroughunboundHandle"
63}
64
65template<
inlineconstT&
operator*()const
66{
67if(ptr)
68return*ptr;
69throwstd:
70}
71
72template<
inlin
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 智能 指针