前言 一晃之间,几个月过去了。想起当初要和几个开发朋友做这个订阅号时的初衷,是有感于自身在学习过程中近乎孤立无援的心路历程。结合现有的少量资源,这个订阅号就仓促诞生了。在每篇内容推出之前,由资料规整,内容挑选和编排,到图片的处理和程序编写都是反复预览和修改之后确定的。尽管如此,还是会有一些小的错误和丑哭的格式排版,但是看到关注人数的不断增加以及后台发过来的种种问题,又更加肯定了要把这件事情做下去。 ! B' C7 W3 y; F8 O 有别于新闻娱乐类推送,知识分享和交流是需要成体系,成块儿存在才会产生较好的效果的。所以,之前的推送基本上是按照这种方式出现的,但基于本人的时间和精力有限,以及当时未能找到一个较好的图文编排工具,做了很多无用功。加之对于内容和形式的苛刻要求,曾有一度感觉到自身被订阅号“绑架”:做好的内容会删删改改,定好的发送日期不断往后推延......后来有幸看到一个乐于分享的程序员的一篇文章(《有时候,沉默是金》),才得以释怀。正如文中所说,这个时代从不缺少信息,感悟,启发和思想,大多数讯息和内容都是过眼云烟,然而却造成了时间和精力的浪费。我自己也是如此,选择关注一个订阅号并坚持阅读它们的内容,是一份信任和认可,相信它们会给我带来我想要的或者我希望去了解的信息。因此,我们在做CATIA二次开发订阅时,更加渴望给关注的每一个朋友们,带来你们想要了解和学习的知识。如果,不能为你们创造价值,不如沉默。 7 t3 i2 T2 | r: Y 所以,再次感谢每一位关注的朋友们,我们会努力为大家提供好文章,尽可能解答后台发过来的疑问。也相信,大家能有所获,让这块园地结出硕果,让我们大家都走得更远!同时,呼吁更多的开发者加入我们,一起分享喜乐。【这段时间,跟几个前辈沟通过,它们表示愿意支持这个事情,在此表示感谢和敬意。】 今天借此机会,跟大家分享一下最近看到的一篇关于C 中“智能指针与引用计数”的介绍。 ' t3 i$ q$ @$ \0 Q `智能指针与引用计数' R$ ]$ [, ]9 N, u* C1简介
由于 C 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete。程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见。
用智能指针便可以有效缓解这类问题,本文主要介绍智能指针的用法。CAA开发中_var即为智能指针,如常见的CATIProduct_var,CATISpecObject_var指针。
对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问智能指针原来的方法则使用“.”操作符。
访问智能指针包含的裸指针则可以用 get() 函数。由于智能指针是一个对象,所以if (my_smart_object)永远为真,要判断智能指针的裸指针是否为空,需要这样判断:if (my_smart_object.get())。(此处,提醒大家在智能指针对象的获取后,最好做一下非空指针的判断。)智能指针包含了 reset() 方法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递一个对象,则智能指针会释放当前对象,来管理新传入的对象。
简单来说,智能指针是对普通指针的一个封装类,其基本用法同普通指针一样,如复制对象时,副本和原对象都指向同一存储区域,如果通过一个副本改变其所指的值,则通过另一对象访问的值也会改变.所不同的是,智能指针能够对内存进行进行自动管理,避免出现悬垂指针等情况。有了这些基本认识之后,再来梳理一下,普通指针在使用的过程中存在的问题。
2智能指针 在C和C 语言中,由于没有内存回收机制,对于new出来的内存块都需要程序员良好的编程习惯去delete进行释放。但往往人是不可靠的,尤其是对于复杂的程序,这个释放操作很容易被遗忘从而造成内存泄漏,或者是提前释放(即悬垂指针:指针指向释放的对象)而导致程序崩溃,而且这种问题一般难以自查。此外,对指针对象重复释放同样会造成内存管理问题。常常在开发过程中,很多人会忽略对内存的管理(有时没有释放指针并不再使用时,程序并不会报错)但对于多线程程序的出现和广泛使用,内存管理不佳的的情况变得更严重,而且在有对程序大小和执行效率有高标准要求时,这种代码编写习惯也会强制被纠正。* u7 K/ A' w, ^9 q 因此,智能指针就是要让开发者专注于指针的使用而将内存的管理交给智能指针去处理,从而减少开发中的致命性错误。关于悬垂指针的产生可查看文末参考文章2。3引用计数 引用计数有效解决了悬垂指针,也称野指针问题。引用的概念大家比较熟悉,那么引用计数是什么呢?简单来说,类比一个场景,A要使用工具箱中某工具,在借用工具时,要在管理员那里进行登记(工作中和生活中类似的场景很常见)。以计数的形式标明工具箱中该工具的数目,借走一个就减1,还回来一个就加1。这种方式能够最快地了解工具箱中公司工具的使用情况,以便借用者完成工具使用的目的。与此类似,引用计数是对指针对象的使用情况计数,已完成对对象的跟踪。它允许有多个相同值的对象共享这个值的实现。 引用计数的主要作用:
- 简化对象跟踪。同一对象的多个所有者在使用对象之后需都要对其delete,而且对象的所有权在不同所有者之间传递,造成了内存的追踪困难。引用计数方式可以自动销毁对象,完成内存的释放。
- 节省内存,提高执行效率。当多个对象具有相同的值时,为每一个对象存储该相同值会造成存储空间的浪费。因此,需要让这些对象共享同一个值的实现。只有当该值被修改时,才进行复制并完成修改(读者自行了解"写时复制"技术)。# N% i& Z* i3 ?
4智能指针的实现 智能指针的实现简言之就是在普通指针的基础上增加了计数器,并将计数器与指针对象进行关联从而实现指针对象的有效管理。智能指针的一般实现包括基础对象类,辅助类和句柄类。基础对象类实现对象的基本属性定义,辅助类成员智能为智能指针使用,完成计数变量的控制。智能指针类是对基础对象类和辅助类的封装。引用计数跟踪指针对象,其作用体现在:
- 当创建类的新对象时,初始化指针,并将引用计数设置为1;
- 当对象作为另一个对象的副本时,复制构造函数复制副本指针,并增加与指针相应的引用计数(加1);
- 使用赋值操作符对一个对象进行赋值时,处理复杂一点:先使左操作数的指针的引用计数减1(为何减1:因为指针已经指向别的地方),如果减1后引用计数为0,则释放指针所指对象内存。然后增加右操作数所指对象的引用计数(为何增加:因为此时做操作数指向对象即右操作数指向对象);
- 析构函数:调用析构函数时,析构函数先使引用计数减1,如果减至0则delete对象。% r5 L. h) H# _5 c4 x, X
<关于C 中智能指针的实现读者可查看参考文章2,本文重点谈论智能指针在CAA开发中的应用>
那么,智能指针和引用计数在CAA开发中是如何体现的呢?
5智能指针及引用计数在CAA开发中的应用 CAA中智能指针和引用计数的应用主要体现在接口或者COM的实现中。CAA中的引用计数方法如下:
下面是各个函数的常见用法:
AddRef()方法的使用
A1.当把一个非空接口指针写到局部变量中时;
A2.当被调用方把一个非空接口指针写到方法或者函数的[out]或者[in,out]参数中时;
A3.当被调用方返回一个非空接口指针作为函数的实际结果时;
A4.当把一个非空接口指针写到对象的一个数据成员中时。
Release()方法的使用
R1.在改写一个非空局部变量或者数据成员之前;
R2.在离开非空局部变量作用域之前;
R3.当被调用方要改写方法或者函数的[in,out]参数,并且参数的初始值为非空时;
#注意:[out]参数往往被假定“输入时值为空”,所以调用方不必释放[out]参数;
R4.在改写一个对象的非空数据成员之前。
引用计数可以精简为以下几个应用场景:
- 当一个非空的接口指针从一个内存位置被拷贝到另一个内存位置应该要调用AddRef,以便通知对象又有附加的引用发生了。
- 对于已经包含非空接口指针的内存位置来说,在重写该内存位置之前,必须要先调用Release,以便通知对象“这个引用已经被销毁”。
- 如果对两个或者多个内存位置之间的关系有特殊的理解的话,那么多余的AddRef和Released调用可以被优化掉。
6小结 在CAA开发中,智能指针常用于接口类对象中,由于智能智能指针能够自动跟总指针对象,用后自动销毁,所以不必担心是否进行内存释放,但结合C 标准库中智能指针的实现可以更好地里了解智能指针的用法和原理。具体应用总结包括:
- 接口类指针定义及初始化,如CATIProduct_var spProd = NULL_var;
- 智能指针访问接口,如CATISpecObject_var spXYPlane = spListRefPlanes[1];
if( spXYPlane != NULL_var ) {
rc = spXYPlane->QueryInterface(IID_CATILinkableObject ,(void **) &pLO_XYPlane); if ( SUCCEEDED(rc) ) {... }
}
- 智能指针赋值,如 CATIProduct_var prod((CATBaseUnknown*)iObj); CATISpecObject_var ThePoint = pIDescendantOnOpenBody1->GetChildAtPosition(1);