专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Ruby教程 » ruby语言:深入理解Ruby语言中的String »正文

ruby语言:深入理解Ruby语言中的String

来源: 发布时间:星期四, 2009年1月8日 浏览:24次 评论:0
  Ruby语言中String是mutable不像java、C#中String是immutable比如

str1="abc"
str2="abc"


  在java中对于字面量jvm内部维持张表因此如果在java中str1和str2是同个String对象而在Ruby中 str1和str2是完全区别对象同样在java中对于String对象操作都将产生个新对象而Ruby则是操纵同个对象比如:

str="abc"
str.concat("cdf")


  此时str就是"abccdf"Ruby对String是如何处理呢?我们只谈谈c ruby中实现有兴趣先看看这篇文章管窥Ruby——对象基础在ruby.h中我们可以看到String对象结构Ruby中对象(包括类也是对象)都是structString也不能例外:

struct RString {
struct RBasic basic;
long len;
char *ptr;
union {
long capa;
VALUE shared;
} aux;
};
//ruby.h


  显然len是String长度;ptr是个char类型指针指向实际串;然后是个联合这个稍后再说如果你看看ruby.h可以发 现几乎所有定义对象结构都有个struct RBasic显然,struct RBasic包含由所有对象结构体共享些重要信息看看RBasic:

struct RBasic {
unsigned long flags;
VALUE klass;
};


  其中flags是个多用途标记大多数情况下用于记录结构体类型ruby.h中预定义了些列比如T_STRING(表示struct RString)T_ARRAY(表示struct RArray)等Klass是个VALUE类型VALUE也是unsigned long可以地将它当成指针(个指针4字节绰绰有余了),它指向个Ruby对象这里以后再深入

  那么联合aux中capa和shared是干什么用呢?RubyString是可变可变意味着len可以改变我们需要每次都根据len 变换来增减内存(使用c中realloc)这显然是个很大开销解决办法就是预留空间ptr指向内存大小略大于len这样就 不需要频繁realloc了aux.capa就是个长度包含额外内存大小那么aux.shared是干什么呢?这是个VALUE类型 介绍说明它是指向某个对象aux.shared其实是用于加快创建速度个循环中:

  ruby 代码

  whiletruedo#无限爀????a重复 a="str"#以“str”为内容创建赋值给a a.concat("ing")#为a所指向对象添加“ing” p(a)#显示“” end

  每次都重新创建个"str"对象内部就是重复创建个char,这是相当奢侈aux.shared就是用于共享char,以字面量创建串会共享个char,当要发生变化时串复制到个非共享内存中变化针对这个新拷贝进行这就是所谓“copy-on-write"技术解释了String内部构造貌似还没有介绍String是如何实现mutable我们写个Ruby扩展测试下我们想写这样个Ruby类:

  ruby 代码

  Testdefteststr="str"str.concat("ing")endend

  对应c语言代码就是:

  cpp 代码

#
#"ruby.h"VALUEt_test(VALUEself){
VALUEstr;str=rb_str_2("str");
prf("beforeconcat:str:%p,
str.aux.shared:%p,str.ptr:%s"n",str,(RSTRING(str)->aux).shared,RSTRING(str)->ptr);
rb_str_cat2(str,"ing");
prf("afterconcat:str:%p,str.aux.shared:%p,str.ptr:%s"n",
str,(RSTRING(str)->aux).shared,RSTRING(str)->ptr);self;
}
VALUEcTest;
voidInit__hack{
cTest=rb__("Test",rb_cObject);
rb__method(cTest,"test",t_test,0);
}//_hack.c


  rb__定义了个类Test,rb__method将t_test思路方法以test名称添加爀????a到Test类

  t_test中通过rb_str_2每次生成个RString结构然后通过rb_str_cat2将str和"ing"连接起来添加

  了些打印用于跟踪利用mkmf产生Makefile,写个extconf.rb

  ruby 代码

  require'mkmf'create_makefile("_hack");

  执行ruby extconf.rb将产生个Makefile,执行make生成_hack.so链接库扩展写完了通过

  ruby:

  ruby 代码

  require'_hack"t=Test.(1..3).each{|i|t.test}

  输出:

before concat: str:0x40098a40, str.aux.shared:0x3, str.ptr:str
after concat: str:0x40098a40, str.aux.shared:0x8, str.ptr:
before concat: str:0x40098a2c, str.aux.shared:0x3, str.ptr:str
after concat: str:0x40098a2c, str.aux.shared:0x8, str.ptr:
before concat: str:0x40098a18, str.aux.shared:0x3, str.ptr:str
after concat: str:0x40098a18, str.aux.shared:0x8, str.ptr:


  从结果可以看出在str concat的前的后str指向位置没有改变改变仅仅是str中ptr指向看看rb_str_cat2实现就目了然了:

  cpp 代码

VALUErb_str_cat(str,ptr,len)VALUEstr;
constchar*ptr;
longlen;
{
(len<0){rb_raise(rb_eArgError,"negativesize(orsizetoobig)");
}
(FL_TEST(str,STR_ASSOC))
{
rb_str_mody(str);
REALLOC_N(RSTRING(str)->ptr,char,RSTRING(str)->len+len);
memcpy(RSTRING(str)->ptr+RSTRING(str)->len,ptr,len);
RSTRING(str)->lenlen;
RSTRING(str)->ptr[RSTRING(str)->len]='"0';
/*sentinel*/
str;
}
rb_str_buf_cat(str,ptr,len);
}
VALUErb_str_cat2(str,ptr)VALUEstr;
constchar*ptr;
{
rb_str_cat(str,ptr,strlen(ptr));
}
//.c


0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: