调用某些FDO的函数,如创建方法,需要申请内存,而这些内存需要在适当的时机释放,以免内存泄漏。FDO使用了引用计数的方式来实现管理对象的生命周期,每个对象都维护着一个引用计数,只有当这个对象的引用计数变为0时,才会去释放这个对象。所以,FDO中每个类都从FdoIDisposable类继承而来的,FdoIDisposable定义了Release()和AddRef()方法。调用方法Release()会将当前对象的引用计数减1,然后判断这个对象的引用计数是否变为0了,如果是则释放这个对象;调用方法AddRef()会将当前对象的引用计数加1。为了防止用于调用操作符delete释放一个对象,FDO中所有类的析构函数都是protected的,调用者只能通过调用Release()方法来释放对象。
1.1.1 内存管理相关的宏
为了简化对象的创建和释放,FDO预定义了如下两个宏。
1. FDO_SAFE_RELEASE (*ptr)
如果“*ptr”不是空指针,FDO_SAFE_RELEASE就会调用“*ptr” 所指对象的Release()方法,而后把指向该对象的指针赋为NULL。该宏定义的实现为:
#define FDO_SAFE_RELEASE(x) {if (x) (x)->Release(); (x) = NULL;} |
该宏的使用方式如下所示。
FdoFeatureClass* pBase = myClass->GetBaseClass(); ... FDO_SAFE_RELEASE(pBase); |
2. FDO_SAFE_ADDREF (*ptr)
如果“*ptr”不是空指针,FDO_SAFE_ADDREF就会调用“*ptr”所指对象的AddRef()方法。该宏定义的实现为:
#define FDO_SAFE_ADDREF(x) ((x != NULL) ? (x)->AddRef(), (x): (NULL)) |
例如当执行代码FDO_SAFE_ADDREF(value)时,如果value是NULL那么返回值也是NULL,否则把value所指对象的引用计数加一并返回value。
1.1.2 智能指针FdoPtr
FdoPtr是用来协助内存管理的智能指针,它是一个C++模板类,它模板参数必须继承自FdoIDisposable的类。用户可以把FDO对象用该指针加以封装,这样,对象就可以在FdoPtr出作用域时自动释放。以下是使用FdoPtr的一个例子:
FdoPtr<FdoFeatureClass> pBase = myClass->GetBaseClass(); ... |
上面的代码无需调用FDO_SAFE_RELEASE,在FdoPtr销毁之前,pBase会调用FdoFeatureClass 对象的Release()方法。
如果由于某些原因我们想对FdoPtr调用FDO_SAFE_RELEASE,我们必须得到FdoPtr封装的指向对象的指针,然后把该指针传入FDO_SAFE_RELEASE。用户可以把FdoPtr用于自定义的类,当然这些类需要继承自FdoIDisposable,并且要实现Dispose()方法。
如果你把调用FDO方法返回的指针赋给了一个非智能指针,那么你应该把这个非智能指针再赋值给一个FdoPtr,例如:
FdoLineString* p = gf.CreateLineString(...); FdoPtr <FdoLineString> p2 = FDO_SAFE_ADDREF(p); |
智能指针为内存管理带来很大的便利,但是如果使用不当反而会产生一些问题,一个比较常见的问题就是链式调用,链式调用会导致返回的指针无法释放。假设pclassDef是指向类FdoClassDefinition实例的一个指针,那么如下的代码会导致两处内存泄露。
psz = pclassDef ->GetProperties()->GetItem(0)->GetName()); |
正确的用法为:
FdoPropertyDefinitionCollection* pprops = pclassDef -> GetProperties(); FdoPropertyDefinition* ppropDef = pprops->GetItem(0); psz = propDef->GetName(); ppropDef->Release(); pprops->Release(); |
或者 (使用FdoPtr)
FdoPtr<FdoPropertyDefinitionCollection> pprops = pclassDef-> GetProperties(); FdoPtr<FdoPropertyDefinition> ppropDef = pprops-> GetItem(0); psz = propDef->GetName(); |
或者(还是使用FdoPtr)
psz = FdoPtr <FdoPropertyDefinition> (FdoPtr <FdoPropertyDefinitionCollection> (pclassDef->GetProperties())-> GetItem(0))->GetName(); |