免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12345
最近访问板块 发新帖
楼主: llj
打印 上一主题 下一主题

delete对象的内存泄露问题 [复制链接]

论坛徽章:
0
41 [报告]
发表于 2011-11-08 15:00 |只看该作者
回复 32# 狗蛋
你的意思我理解,但是字面上来说毕竟有反例,所以我说不严密。
这种思想在C++中有一个常用的idiom:资源绑定到一个对象的生存期上,称为RAII。
不过,这时“这个一一对应并不仅仅是有申请就有释放,而是怎么申请就怎么释放”就又有问题了。

  1. #include <memory>

  2. int main()
  3. {
  4.     std::shared_ptr<int> ptr(new int);
  5.     return 0;
  6. }
复制代码
如何?当然,释放还是是有的。

论坛徽章:
0
42 [报告]
发表于 2011-11-08 16:13 |只看该作者
本帖最后由 狗蛋 于 2011-11-08 16:14 编辑

回复 41# 幻の上帝


    这就是另一回事了。可以说是c++的福利,也可以说是它为了制造混乱而孜孜不倦的努力所制造的又一成果——或者说,微软式封装的又一成果。

java 更绝,它还只有new,你就是想delete都没辙。但我们现在毕竟没说java。




至于讨论一般化的资源管理,有以下几点:

首先,资源申请和释放要一一对应、并且申请和释放的形式都要对称:这是手动管理资源的根本原则。
至于资源究竟是什么,那就说不上来了。可能的情况太多了。


其次,当我说“苹果怎么削”时,当然和“橘子怎么剥”狗屁关系没有。甚至和“梨子的削皮”这样同样是“可削皮水果的削皮方法”无关;至于”土豆如何削皮“,那就远的无聊了。
换言之,我说”new要和delete一一对应“时,当然指的就是new/delete关键字本身,和placement new没关系。
你要说有关系也成,但那是你自己的placement new要和”与placement new绑定的那个delete(placement delete?)一一对应“——而不是拿来和我的new掺杂不清:不好意思,我现在只削苹果。梨子先一边靠着去。

再次,资源申请和释放形式要对称,这是为了让资源管理方案清晰、便于排错。
正如我之前所说的,当你用宏来new时,最好也用宏来delete。
但,如果你要刻意违反这点,那也无所谓。只是你必须明白自己在做什么、有什么风险——前面说过了,java还有new无delete呢。只是我们都知道它有垃圾回收,如此而已。
当然,万能的C++当然也可以模拟垃圾回收。此时delete自然就不应该出现——但这和“手动资源管理”有蛋关系?!

最后,有new无delete的设计正常吗?提供了什么便利?其它多少语言都做到了无new无delete(同样有垃圾收集功能!),它们和java相比,在相关方面有什么劣势?或者说,这种有new无delete的设计,是某个功能的前提、还是抽象上的残疾,或者干脆是扯淡?
——管它是哪种,反正和new/delete以及手动资源管理无关。

论坛徽章:
0
43 [报告]
发表于 2011-11-08 21:06 |只看该作者
本帖最后由 幻の上帝 于 2011-11-08 21:16 编辑

回复 42# 狗蛋

>>当我说“苹果怎么削”时,当然和“橘子怎么剥”狗屁关系没有。
然而你说的都是或剥或削的问题。那么干嘛需要明确是苹果还是土豆呢。

>>换言之,我说”new要和delete一一对应“时,当然指的就是new/delete关键字本身,和placement new没关系。
意思我还是清楚,但这你又错了。关键字本身不考虑具体用法对资源管理来说是不合时宜的,你不得不明确你是在怎么new,然后才能谈你的形式上的对称。你说的是new expression/delete expression,然而placement也有两重含义:new expression语法上的placement(也就是紧跟在new后面的一堆(!@$#)),还有就是operator new(sz, !@#$)以及operator delete(p, !@#$)。说道某种具体的实现方式,你可以拒绝使用placement,但是泛泛地谈new的时候你还是回避不了。

>>再次,资源申请和释放形式要对称,这是为了让资源管理方案清晰、便于排错。
这点我很同意。不过,有两个绝对不可能对称的地方。
第一,就是我在41L举的例子,绑定到自动对象上的RAII。不过,隐式析构体现的语义和使用的技巧和微软式的封装毫无关系。而自动对象本身来源于C。
第二,注意我刚才说的。new expression有placement,但delete expression没有这种选项。偏偏operator delete是有placement的,用于对应形式的new expression抛出异常(注意默认 :: operator new的placement不抛异常,这里是初始化对象时抛的异常)的处理。这种不对称是既定的语言特性。
关于第一种情形的显然的不对称的话题,等会再讲。

>>当然,万能的C++当然也可以模拟垃圾回收。此时delete自然就不应该出现——但这和“手动资源管理”有蛋关系?!
垃圾回收和“手动资源管理”是没关系,不过,什么情况下算是“手动”,有待商榷。
垃圾回收之所以不算手动,是因为对象所有权扔给垃圾回收器以后可以不管什么时候会被析构了——而不只是可以不写delete。所谓不具有确定性的析构
但是,上面的RAII不一样。在块作用域中写下std::shared_ptr<int> ptr(new int);这样的代码时,用户需要明明白白地清楚这里new出来的东西将会在ptr的作用域结束时析构,而不仅仅是“不用写delete”。而这里不写delete的原因是编译器对于自动对象产生隐式析构的代码,给重用(这里是std::default_delete)提供了可能。这的确可以是福利。但解放了你的手不等于你可以不用思考。这里不能排除“手动”的成分,意即用户必须明确什么时候析构,乃至明确整个对象的生存期。
关于这里显然的不对称的话题,仍然等会再讲。

>>最后,有new无delete的设计正常吗?提供了什么便利?其它多少语言都做到了无new无delete(同样有垃圾收集功能!),它们和java相比,在相关方面有什么劣势?或者说,这种有new无delete的设计,是某个功能的前提、还是抽象上的残疾,或者干脆是扯淡?
对称性的问题放到这里来讲。
先说刚才提到的第二种情形,关于为什么没有delete placement expression。我记得BS的主页上有提到过,既然已经有了显式析构函数调用,那就不需要了。这样的设计或许还真算是残疾——至少不能算优雅。
然后是RAII。关键在于,为什么释放资源能够使用隐式的代码。原因很简单:析构函数和构造函数不可能保证对称。其实上面的情形也多少受到这个影响。
不过,真正麻烦的原因不在这里。你说有些语言可以无new无delete——无delete是容易设计得出来(可以扔给运行时库然后用户什么都不用管了),但即使没有new,也存在替代品,包括类似C的自动对象的玩意儿——除非那种语言不用显式声明和定义使用存储的实体(在类C语言里就是对象)并据此确定它的生存期起始,或者不这样做但是能对用户提供特性取消声明/定义的作用同时在其上附加隐式终止生存期的语义。类C语言看来是没法突破这个框框的。
在扯远点还有很多东西证明不对称的普遍性。T-SQL里面可以drop掉一个函数,类C语言即使没有声明定义的规则的限制,从实现上来说也是没办法的,因为这里的函数是编译时静态地确定的。拿构造→使用成员→析构类比,编译→运行→??(反编译?)显然不太通。(……跑题太远我自重。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP