在上一篇文章中,我们提到过,在一个对象的析构函数中执行太多任务,可能导致对象被释放两次。解决此问题的标准方法是在析构过程中使用一个自定义的引用计数,如下图所示:
>> 请移步至 topomel.com 以查看图片 <<
如果你有 IUnknown 接口的通用实现,则可以像我们在此处所做的那样,在 IUnknown 接口的 Release 方法中将引用计数设置为 DESTRUCTOR_REFCOUNT,并在实现的析构函数中断言该值是正确的。
由于 C++ 在派生类析构函数之后运行基类析构函数,因此基类析构函数将在派生类完成清理后检查引用计数。
通过将引用计数设置为人为的非零值,发生的任何 AddRef 和 Release 调用都不会触发重复销毁(当然,假设析构函数路径中没有人有导致它们过度释放的错误)。末尾的断言可确保在销毁过程中不会创建对对象的新引用。
这实际上更像是一种解决方法,而不是坚如磐石的解决方案,因为它假定在销毁序列期间调用的任何函数都不会保留对函数返回之外的对象的引用。这通常不是你可以假设的关于 COM 的事情。通常,方法可以自由调用 AddRef 并挂在指向对象的指针上,以便稍后完成请求的操作。
某些方法(如 IPersistPropertyBag 接口的 Load 方法) 明确禁止此类行为,但这些类型的方法更像是例外而不是规则。
课后练习题
为什么执行简单的赋值操作 m_cRef = DESTRUCTOR_REFCOUNT,而不是更复杂的互锁交换 InterlockedExchangeAdd(&m_cRef,DESTRUCTOR_REFCOUNT),这样岂不是更安全的?
总结
图中的技法,可以加以利用,以确认对象的析构函数确实仅会被释放一次。
你的技能工具箱里又多了一把趁手的小玩意儿了。
最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《Avoiding double-destruction when an object is released》