当对象的引用计数为零时

news2025/1/21 9:41:13

上一篇文章,我提到要避免对象的析构函数被调用两次,有一位读者声称:当对象第一次被构建的时候,它的引用计数应该为 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》

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434452.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【算法题解】24. 模拟机器人行走

这是一道 中等难度 的题 https://leetcode.cn/problems/walking-robot-simulation/description/ 题目 机器人在一个无限大小的 XY 网格平面上行走&#xff0c;从点 (0, 0) 处开始出发&#xff0c;面向北方。该机器人可以接收以下三种类型的命令 commands &#xff1a; -2 &am…

C++ 引用

什么是引用 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。&#xff08;语法层面来讲&#xff09; 但在底层实际上引用是开辟空间的&#xff0c;类似于指针 …

大数据能力提升项目|学生成果展系列之八

导读 为了发挥清华大学多学科优势&#xff0c;搭建跨学科交叉融合平台&#xff0c;创新跨学科交叉培养模式&#xff0c;培养具有大数据思维和应用创新的“π”型人才&#xff0c;由清华大学研究生院、清华大学大数据研究中心及相关院系共同设计组织的“清华大学大数据能力提升项…

操作系统前置知识

进程 程序存储在磁盘之中&#xff0c;需要加载内存才能执行&#xff0c;包含堆空间、栈空间、全局和静态变量数据、代码&#xff0c;具体执行效果如下所示&#xff1a; 所谓的进程概念就是操作系统为了执行某个程序为其分配的内存资源&#xff0c;该内存资源并不是连续的&…

【数据结构】二叉树的链式结构(笔记总结)内附递归展开图(炒鸡详细)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;数据结构 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&…

Web前端基础——盒子模型

&#xff08;1&#xff09;盒子模型的作用&#xff1a; 布局网页&#xff0c;摆放盒子和内容 &#xff08;2&#xff09;盒子模型重要组成部分&#xff1a; 内容区域 - width & height内边框 - padding&#xff08;出现在内容与盒子边缘之间&#xff09;边框线 - border外…

毕业2年,跳槽到下一个公司就25K了,厉害了···

本人本科就读于某普通院校&#xff0c;毕业后通过同学的原因加入软件测试这个行业&#xff0c;角色也从测试小白到了目前的资深工程师&#xff0c;从功能测试转变为测试开发&#xff0c;并顺利拿下了某二线城市互联网企业的Offer&#xff0c;年薪 30W 。 选择和努力哪个重要&a…

建模技能C位秘诀 | 装配式建筑操作技能

剪力墙结构PC构件-预制剪力墙 YUGOU SCHOOL 1、承载力计算&#xff1a;对一、二、三级抗震等级的装配式剪力墙结构&#xff0c;应进行剪力墙水平接缝的抗震受剪承载力验算。 由公式可以看出预制剪力墙水平抗剪主要是靠垂直穿过结合面的竖向抗剪钢筋以及结合面上的轴向压力&a…

RSA-2048-Encoded-Modulus

裸公钥和x509格式公钥的区别 (公钥&#xff0c;非证书) x509 30820122300D06092A864886F70D01010105000382010F003082010A02820101||00 || 256字节的modulus||0203010001 解析: 0203010001 tag length value 结构 &#xff0c;pubExponent 010001 大于7F补 00 &#xff1f;…

C++11多线程:原子操作std::automic-用于多个线程之间共享的变量。

系列文章目录 文章目录 系列文章目录前言一、std::automic二、使用步骤1.代码案例 总结 前言 原子操作std::automic的基本概念和用法。 一、std::automic std::atomic来代表原子操作&#xff0c;std::automic是个类模板。其实std::atomic这个东西是用来封装某个类型的值的。 …

常用 Composition API--ref函数

ref函数--处理基本类型 以前我们的ref属性用处主要用于打标识&#xff0c;像原生js中的id标签一样。我们可以通过这个ref函数可以实现获取input元素&#xff0c;并让他获取焦点触发事件 而在v3中的是ref函数 先提出一个例子&#xff0c;我点击一个按钮&#xff0c;但是页面并…

WordCount 在 MapReduce上运行详细步骤

注意&#xff1a;前提条件hadoop已经安装成功&#xff0c;并且正常启动。 1.准备好eclipse安装包&#xff0c;eclipse-jee-juno-linux-gtk-x86_64.tar.gz&#xff0c;使用SSH Secure File Transfer Client工具把安装包上传于Hadoop集群的名称节点。 2.上传Hadoop在eclipse上运…

C++缺省参数的具体使用

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C之路】 本文来详细介绍C中的缺省参数。正文开始&#xff1a; 目录 一、缺省参数概念二、缺省参数分类2.1全缺省2.2半缺省 三、缺省参数…

Linux上搭建Discuz论坛

一.准备工作 1.下载php*&#xff0c;mariadb-server 2.上传Discuz3.5压缩包并解压 二.搭建过程 基于redhat 9 版本和Discuz3.5&#xff0c;php8.0&#xff0c;mariadb10.5演示 一.准备工作 1.下载php*&#xff0c;mariadb-server [rootredhat9 aaa]# yum install -y php*…

300元的蓝牙耳机什么牌子好?300内无线蓝牙耳机推荐

感受过无线的自在舒适后&#xff0c;越来越多的小伙伴爱上了蓝牙耳机白天出街更潇洒&#xff0c;目前市面上蓝牙耳机琳琅满目可选择性较多价格从几十、几百元到数千元不等然而蓝牙耳机的安全性、舒适性如何&#xff1f;连接稳吗&#xff1f;下面整理了几款300元价位的耳机分享给…

前端配置项

默认下载安装所需插件的时候会自动添加配置&#xff0c;但有时候可能需要自己去配&#xff0c;比如系统重装&#xff08;重装的是C盘&#xff0c;系统变量会丢失&#xff0c;软件在其他盘符并不需要重新下载&#xff09;。 前端开发涉及到需要配置变量的软件有&#xff1a;git…

JVM之GC日志解读

通过阅读Gc日志&#xff0c;我们可以了解Java虚拟机内存分配与回收策略。 内存分配与垃圾回收的参数列表 -XX:PrintGC 输出GC日志。类似&#xff1a;-verbose:gc -XX:PrintGCDetails 输出GC的详细日志 -XX:PrintGCTimestamps 输出GC的时间戳&#xff08;以基准时间的形式&#…

如何进行帕累托分析

【面试题】有一张“学生成绩表”&#xff0c;包含3个字段&#xff1a;学号、课程、成绩。 问题&#xff1a;找出每门课程A类和B类的学生&#xff0c;判断标准是累计占比&#xff0c;0~60%的记为A类&#xff0c;60%~85%记为B类 【解题思路】 什么是二八定律&#xff1f; 二八定律…

Java通过显示弹奏音乐的方式来实现继承的有关方法

目录 前言 一、Music.java类 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 二、Brass.java类 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 三、Wind.java类 1.1运行流程&#xff08;思想&#xff09; 1.2代码段 四、Instrument.java类 1.1运行流程&…

世界领先的电动汽车国际标准 一 ISO 15118全系列

世界领先的电动汽车国际标准 一 ISO 15118全系列 ISO 15118 的官方名称是“道路车辆——车辆到电网的通信接口”。我可能有点偏见&#xff0c;因为我是该国际标准的共同作者之一&#xff0c;但我坚信 ISO 15118 是当今可用的最重要和面向未来的标准之一。 ISO 15118 中内置的…