免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4082 | 回复: 4
打印 上一主题 下一主题

长跳破坏 RAII 的简单例子 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-07-21 13:35 |只看该作者 |倒序浏览
转:night_stalker

长跳破坏 RAII 的简单例子



Resource Acquisition Is Initialization 是 C++ 卖点之一, 简单来说就是退出局部作用域的时候局部变量的析构函数就会被自动调用. 在 RAII 特性上可以做自动指针, 可以实现类似 GC 的功能, STL 和 Boost 都有智能指针和自动指针的实现.

但是如果作用域里调用的函数用了长跳转跳出去了, 析构函数就不会被调用, 内存泄漏就由此而起, 而且 ... 长跳转其实在 Ruby 里到处都是 ... 例如:


Ruby代码
  1. def find_first_a list
  2.   list.each {|i| return 'a' if i == a } # return in proc => long jump!
  3.   return false
  4. end
复制代码
下面用一个简单的 extension 验证看似正确的代码是怎么内存泄漏的 ...

ra.cpp

C++代码
  1. #include "iostream"
  2. #include "ruby.h"

  3. namespace {
  4.   // 典型的 C++ 类: 在构造函数分配资源, 在析构函数销毁资源
  5.   struct C {
  6.     int *a;
  7.     C() {
  8.       a = new int[100];
  9.       std::cout << "constructor called\n";
  10.     }
  11.     ~C() {
  12.       delete[] a;
  13.       std::cout << "destructor called\n";
  14.     }
  15.   };

  16.   VALUE f(VALUE self) {
  17.     // 构造函数被调用
  18.     C c;
  19.     // block 参数
  20.     VALUE args[0];
  21.     // 如果 block 中用了 return 产生了长跳转, 下面的代码都不会被调用
  22.     rb_yield((VALUE)args);
  23.     return Qnil;
  24.     // RAII 隐藏了在这里调用 c 的析构函数的代码
  25.   }
  26. }

  27. extern "C"
  28. void Init_ra() {
  29.   VALUE c = rb_define_class("C", rb_cObject); // 定义 Ruby 类 C
  30.   rb_define_method(c, "f", RUBY_METHOD_FUNC(f), 0); // 定义 Ruby 方法 C#f
  31. }
复制代码
编译生成扩展库 (ra.so 或 ra.bundle)

Console代码
  1. ruby -rmkmf -e "create_makefile 'ra'"
  2. make
复制代码
测试长跳 (同一目录下的 test.rb)

Ruby代码
  1. require_relative "ra"

  2. def raii_broken
  3.   C.new.f {
  4.     return # long jump !
  5.   }
  6. end

  7. def raii_normal
  8.   C.new.f {}
  9. end

  10. puts "RAII broken:"
  11. raii_broken
  12. puts "\nRAII normal:"
  13. raii_normal
复制代码
结果: 不用 return 就没泄漏, 用 return 就泄漏了

Console代码
  1. → ruby -v
  2. ruby 1.9.3dev (2011-07-10 trunk 32499) [x86_64-darwin10.8.0]
  3. → ruby test.rb
  4. RAII broken:
  5. constructor called

  6. RAII normal:
  7. constructor called
  8. destructor called
复制代码
补充:
GCC 的 cleanup attribute 扩展也是 RAII, 还没时间试验, 估计也是 ...

论坛徽章:
0
2 [报告]
发表于 2011-07-21 22:46 |只看该作者
支持村艹了。

论坛徽章:
0
3 [报告]
发表于 2011-07-23 18:28 |只看该作者
但这个应该不太用得上。

论坛徽章:
0
4 [报告]
发表于 2011-08-01 23:00 |只看该作者
主要是用C++来写扩展的时候,要注意。
本来,用C++编程时,是一直不建议和setjmp,longjmp一起使用的。因为setjmp和longjmp不支持 C++的对象语义。

论坛徽章:
0
5 [报告]
发表于 2011-08-02 08:28 |只看该作者
村艹咋不发了?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP