上一篇文章,我提到要避免对象的析构函数被调用两次,有一位读者声称:当对象第一次被构建的时候,它的引用计数应该为 0,在某些时候,例如调用 QueryInterface 的时候,它的 AddRef 方法应该被调用以增加其引用计数。

如果在构造一个对象时将它的引用计数设置为 0,你有点像在玩火柴。对于新手来说,当对象被创建的时候,它的引用计数不应该为 0,因为创建此对象的人拥有这个对象的一个引用。

请记住,对于 COM 对象来说,它的引用计数规则是:当一个函数产生一个引用(通常是一个接口指针)时,引用计数会增加。如果你将构造函数看做是一个函数的话,则它需要增加引用计数的值,来体现它创造了一个对象这样一个事实。

如果你更喜欢玩火柴,你最终可能会用如下代码烧到自己:

>> 请移步至 topomel.com 以查看图片 <<

请注意,在上面的代码中,我们将对象的引用计数初始化为 0。这使你处于与清理对象相同的“边缘区域”,而该对象引用计数为零,因此你会面临相同的问题:

>> 请移步至 topomel.com 以查看图片 <<

在销毁过程中保存自身的对象很可能在创建过程中加载自身。你遇到了完全相同的问题。对 IObjectWithSite::SetSite(this) 的调用会将对象的引用计数从 0 增加到 1,而对 IObjectWithSite::SetSite(NULL) 的调用会将其递减为零。当引用计数递减到零时,这将销毁对象,从而导致对象被 MyObject::Load 方法无意中销毁。

MyObject::Create 静态方法没有意识到这种情况已经发生,并继续调用 QueryInterface 方法以将指针返回给调用方,期望它将引用计数从 0 增加到 1。不幸的是,它正在对已经被摧毁的对象执行此操作。

当你使用引用计数为零的对象时,就会发生这种情况:当你放弃控制权时,它可能会消失。创建的对象应具有 1 的引用计数,而不是零。

ATL更喜欢玩火柴,在其对象构造中使用上述 MyObject::create 函数的道德等价物:

>> 请移步至 topomel.com 以查看图片 <<

ATL 会调用引用计数为零的 FinalConstruct 方法来构造一个 COM 对象。如果你知道这种方式的潜在危险,则可以使用
DECLARE_PROTECT_FINAL_CONSTRUCT 宏将 InternalFinalConstructAddRef 和 InternalFinalConstructRelease 方法更改为在调用 FinalConstruct 期间实际临时增加引用计数的版本,然后在 QueryInterface 调用之前将引用计数放回零(不破坏对象)。

它有效,但在我看来,它过于依赖程序员的警惕性。ATL 的默认设置是将选择权交给程序员,并依靠程序员“知道”FinalConstruct中可能发生危险的事情,并且有意识地要求
DECLARE_PROTECT_FINAL_CONSTRUCT。换句话说,它选择了危险的默认值,程序员必须明确要求安全版本。但是程序员有很多事情要做,强迫他们考虑在 FinalConstruct 方法中执行的每个操作的传递闭包的后果是一个不合理的要求。

考虑我们上面的例子。最初编写代码时,Load 方法可能要简单得多,如下图所示:

>> 请移步至 topomel.com 以查看图片 <<

直到一两个月后,才有人向加载和保存方法添加了站点支持。这个看似简单而孤立的更改,完全遵守了引用计数的 COM 规则,在对象创建和销毁代码路径中产生了连锁反应。如果在 FinalConstruct 和 Load 之间放置四个级别的函数调用,那么这种第四级调用器效应很容易被忽略。我怀疑这些非局部效应是代码缺陷的最重要来源之一。ATL很聪明,优化了一个增量和一个递减(编译器很可能可以自己优化出来),但作为回报,你得到了一盒”火柴盒”。

(我并不是要在这里挑剔ATL,它试图设计的又小又快,但代价是增加了复杂性,并且这种复杂性是很微妙的,难以一眼就能理解的)

总结

使用引用计数来管理对象的生命周期,我一直是比较抗拒的。我总是担心会发生一些令人意想不到的事情(比如发生了地震导致CPU上的一根引脚短路),导致了本该释放的对象因为引用计数的计算错误而没有得到释放,或者本不该释放的对象,因为引用计数而提前释放。
我不能将一个随时可能崩溃的程序交付给我的用户,绝不。
如果正在阅读本文的大大(你)有什么好的妙招,希望能对愚钝的我指教一二。

最后

Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《On objects with a reference count of zero》

当对象的引用计数为零时相关推荐

  1. python学习高级篇(part9)--对象的引用计数

    学习笔记,仅供参考,有错必纠 文章目录 python 学习高级篇 类对象的特殊方法之`__str__()` 类对象的特殊方法之`__new__()` 对象的引用计数 什么是引用计数 对象的引用计数加1 ...

  2. iOS内存管理系列之一:对象所有权与引用计数

    原文地址:[转]iOS内存管理系列之一:对象所有权与引用计数作者:anynot 内存管理是iPhone或iPad开发中最为重要的一部分.掌握好了内存管理,开发出的应用就能运行流畅:掌握不好,开发出的东 ...

  3. Jvm(20),如何定义为垃圾对象----引用计数法

    假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请我们一起开启提升技术之旅吧,详情 ...

  4. 【Netty4】netty ByteBuf (二) 引用计数对象(reference counted objects)

    原文出处:http://netty.io/wiki/reference-counted-objects.html 相关文章: netty ByteBuf (一)如何创建ByteBuf对象 netty ...

  5. 【Netty官方文档翻译】引用计数对象(reference counted objects)

    原文出处:http://netty.io/wiki/reference-counted-objects.html 原文地址可能有变,且内容可能发生变化. 如果转载请注明出处,谢谢合作^_^. 自从Ne ...

  6. Reference counted Objects (引用计数对象) - 文章翻译

    原文地址:http://netty.io/wiki/reference-counted-objects.html 从Netty4开始,某些对象的饿生命周期由其引用计数来管理,因此,一旦不再使用,Net ...

  7. python 引用计数 循环引用_Python对象的循环引用问题

    Python对象循环引用 我们来介绍一下 Python 是采用何种途径解决循环引用问题的. 循环引用垃圾回收算法 上图中,表示的是对象之间的引用关系,从自对象指向他对象的引用用黑色箭头表示.每个对象里 ...

  8. 提高C++性能的编程技术笔记:引用计数+测试代码

    引用计数(reference counting):基本思想是将销毁对象的职责从客户端代码转移到对象本身.对象跟踪记录自身当前被引用的数目,在引用计数达到零时自行销毁.换句话说,对象不再被使用时自行销毁 ...

  9. VTK修炼之道80:VTK开发基础_智能指针与引用计数

    1.引用计数 VTK经过多年的开发与维护,已经形成了一套稳定的框架和开发规则.因此,了解这些规则和框架是定制VTK类的基础,这其中用到了大量面向对象的设计模式,例如对象工程模式.观察者/命令模式:还有 ...

最新文章

  1. Python 远程连接服务器用它就够了
  2. python ftp文件传输服务端
  3. 定义系统消息 Specify system messages
  4. 机器学习之手把手实现,第 2 部分 频繁项集与关联规则 FP-growth 的原理和实现...
  5. 坚持,这两个字非常重要!
  6. 动态规划——硬币找零思路
  7. win10命令提示符怎么打开_Win10系统防火墙怎么打开?ARP防火墙启用步骤
  8. 【POJ - 2406】Power Strings (KMP,最小循环节)
  9. 机器学习-吴恩达-笔记-2-逻辑回归
  10. 批处理学习之Bat命令——获取当前盘符、当前目录、上级目录
  11. bzoj 1072: [SCOI2007]排列perm
  12. r语言 转录本结构及丰度_技术贴 | R语言:envfit环境因子和菌群回归分析
  13. 计算机硬件未来发展前景,计算机硬件的未来发展趋势
  14. 亿万用户网站MySpace的成功秘密
  15. 5个PPT素材、模板网站,建议收藏~
  16. 国考计算机及其应用科目,2018国家公务员考试专业科目考试大纲(计算机类)
  17. Three.js加载外部模型骨骼动画
  18. 1228|如何用ALV输出完成SAP报表
  19. 夏季吃5种水果对抗紫外线
  20. 键盘上的顿号怎么打出来

热门文章

  1. 网银,快钱,支付宝的区别
  2. Spring 有几种事务隔离级别?
  3. Mybatis接收Map传值
  4. 自学编程C语言不迷路,我私藏的书单分享给你!
  5. 如何在短期快速学好英文
  6. VLC和WebRTC等开源库关于硬解软解的策略
  7. 在华清远见的学习感悟
  8. 兔子笑话联想到的哲理
  9. 一文看懂博睿数据AIOps场景、算法和能力
  10. presto cli 命令行使用