Project1
标题:
关于Viewport的诡异测试
[打印本页]
作者:
沉影不器
时间:
2009-9-15 20:07
提示:
作者被禁止或删除 内容自动屏蔽
作者:
紫苏
时间:
2009-9-15 23:10
clone 的调用是成功了的,克隆后的对象 id 也不同,只不过通过克隆体调用 set 或者 width= 的时候引用的是本体的变量,而调用本体的 width= 就冇问题了:
v = Viewport.new(0,0,12,12)
a = v.rect
b = a.clone
c = b.clone
a.width = 9999
p a, b, c
复制代码
可见至少 width 和 height 方法确实返回的是正确克隆后的宽度和高度变量
这个应该还是和 Rect 的属性在 C 中实现有关,因为 Rect 本身没有覆盖 Object 的 clone 方法,而 Sprite啊、Viewport啊之类的都覆盖了的,所以下面 p 出来是 false 的类都应该有这个问题:
p "Rect #{Rect.instance_methods(false).include?("clone")}",
"Sprite #{Sprite.instance_methods(false).include?("clone")}",
"Bitmap #{Bitmap.instance_methods(false).include?("clone")}",
"Viewport #{Viewport.instance_methods(false).include?("clone")}",
"Plane #{Plane.instance_methods(false).include?("clone")}",
"Color #{Color.instance_methods(false).include?("clone")}",
"Font #{Font.instance_methods(false).include?("clone")}",
"Window #{Window.instance_methods(false).include?("clone")}",
"Tilemap #{Tilemap.instance_methods(false).include?("clone")}",
"Tone #{Tone.instance_methods(false).include?("clone")}",
"Table #{Table.instance_methods(false).include?("clone")}"
复制代码
作者:
沉影不器
时间:
2009-9-16 21:32
提示:
作者被禁止或删除 内容自动屏蔽
作者:
紫苏
时间:
2009-9-16 23:04
哦,原来覆盖是为了禁止 clone,还真不知道~
……不能 Marshal 确实诡异,无源码无从分析
不过我发现只要不是在 RGSS 类内部(比如 Viewport 的构造函数)分配的某个属性对象,至少 clone 可以正常工作了:
a = Rect.new(0, 0, 12, 12)
v = Viewport.new(0, 0, 1, 1)
v.rect = a
b = v.rect.clone
b.width = 999
p a, b
复制代码
另外不仅仅是 rect,只要是这些 RGSS 类的对象属性都有这个问题:
s = Sprite.new
a = s.color
b = a.clone
b.red = 88
p a, b
复制代码
p = Plane.new
a = p.color
b = a.clone
b.red = 999
p a, b
复制代码
对象 id 不同,说明在 Ruby 层的克隆成功了,只不过如果这个对象是在 C 层上分配的话,推测就是没有克隆数据,而是克隆了指针(?!)
作者:
霜冻之狼
时间:
2009-9-18 17:46
好东西,俺来膜拜下
作者:
IamI
时间:
2009-9-18 17:53
如果我这么说阁下觉得可以理解吗
def rect
return Rect.new(x,y,width,height) # 伪代码,实际公式要比这个复杂= =
end
def rect=(value)
# 同
end
复制代码
这个方法只是为了方便使用而已……临时对象……结果自然可想而知
作者:
link006007
时间:
2009-9-18 19:36
.clone方法返回的的确是Viewport.rect的属性的拷贝,
但是clone方法返回的是一个"冻结的,且会感染原实例"的实例(我英文不好- -!!)...
也就是clone产生的实例被修改了,会影响到原实例. 但是源实例被修改了,则clone产生的实例不会受到影响
a = Viewport.new(0,0,12,12)
b = a.rect.clone
a.rect.width = 24
p a.rect, b, "ID:", a.rect.object_id, b.object_id, "Ins:", a.rect.inspect, b.inspect
b.width = 48
p a.rect, b, "ID:", a.rect.object_id, b.object_id, "Ins:", a.rect.inspect, b.inspect
c = a.rect.clone
a.rect.width = 12
p a.rect, c, "ID:", c.object_id, "Ins:", c.inspect
c.width = 48
p a.rect, c, "ID:", c.object_id, "Ins:", c.inspect
exit
复制代码
还有 Viewprot不能被clone,并不一定要重载clone方法
如果Viewport定义的ruby类具有const属性, 那么根据const的原则,是不可以存在一个可以感染它的clone实例,也就是Viewport不能被clone的原因
VALUE
rb_obj_clone(obj)
VALUE obj;
{
VALUE clone;
if (rb_special_const_p(obj)) {
rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj));
}
... ...
... ...
return clone;
}
复制代码
以上只是个人理解... 实际还要去看源代码- -
作者:
紫苏
时间:
2009-9-18 21:07
clone 只是会拷贝被克隆对象的污染和冻结状态,并不是克隆后必然产生污染和冻结的对象 ^^
v = Viewport.new(0, 0, 12, 12)
a = v.rect
b = v.rect.clone
p a.frozen?, b.frozen?
p a.tainted?, b.tainted?
复制代码
另外即便是污染或者冻结,前者和安全等级有关,禁止了对对象的一些操作;而后者是使对象不可修改,“clone产生的实例被修改了,会影响到原实例. 但是源实例被修改了,则clone产生的实例不会受到影响”这个结论是从何而来?
作者:
link006007
时间:
2009-9-18 22:43
“clone产生的实例被修改了,会影响到原实例. 但是源实例被修改了,则clone产生的实例不会受到影响”
紫苏 发表于 2009-9-18 21:07
我前面已经说了 这个只是我的理解...
Copies the frozen and tainted state of OBJ.
也就是第一段代码的运行结果.
a = Viewport.new
b = a.rect.clone
c = a.rect.clone
b或c修改,a的值会改变. 而a的值修改,b与c值不改变
再如
class Klass
attr_accessor :str
end
s1 = Klass.new #=> #<Klass:0x401b3a38>
s1.str = "Hello" #=> "Hello"
s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello">
s2.str[1,4] = "i" #=> "i"
s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">"
复制代码
但是如果单独的Rect.new.clone就不会有这种效果 = =
作者:
紫苏
时间:
2009-9-18 23:03
呵呵,我前面也说了,clone 只是拷贝被克隆体的污染和冻结状态而已,如果被克隆体本身就不是污染和冻结状态,克隆之后自然也不是……所以上面给出了调用 tainted? 和 frozen? 方法的代码,就是为了看克隆体是否有这两个状态的~
另外 Klass 这段代码和这贴讨论的不搭界——
首先 s1 是一个 Klass 的实例,并让其 str 成员指向了 "Hello" 对象;s2 是 s1 的克隆体,拷贝了其所有的成员变量,但这是一个浅层拷贝,s2 的 str 成员实际上指向的还是 s1 的 str 对象,也就是之前的那个 "Hello",换句话说就是这里的浅层拷贝仅仅拷贝的是对象在栈中的引用,而并没有在堆中实际拷贝一份对象的数据(值)~
class Klass
attr_accessor :str
end
s1 = Klass.new
s1.str = "Hello"
s2 = s1.clone
p s1.str.id, s2.str.id # 对象 id 一致
复制代码
而这贴讨论的 Rect 问题,其属性按理说只有四个整型变量,那么普通的浅层拷贝就能做到值的拷贝了,但实际上还是有我们都看到的这个问题……(所以上面我才猜测可能是和拷贝了指向整型数据的指针有关)
作者:
link006007
时间:
2009-9-18 23:10
本帖最后由 link006007 于 2009-9-18 23:19 编辑
哦 我在看看...
另外
str的id一样 是因为
像ruby这种高级语言 他们对字符串的处理都是用一个叫做"字符串常量池"的东西在维护.
即相同的字符串,他们其实引用的是一个字符串内存.. 这个和Java相似.
s1和s2应该是不一样的吧...
作者:
紫苏
时间:
2009-9-18 23:24
1、s1 和 s2 当然不一样,但是它们引用到的是一个对象,如果深层拷贝的话,他们引用的就应该是不同的对象
2、那你就错了,Java 的确有常量池,其字符串有保留(intern)机制,但 Ruby 没有~
p "hello".id, "hello".id
复制代码
这里就生成了两个一模一样的 "hello",完全没有全局唯一性,只有取值范围在 31 位带符号整数之内的整型 Ruby 对象才有全局唯一性:
p 1.id, 1.id
复制代码
3、是的,所以 Rect 的基本数据类型按理说应该可以直接浅层拷贝(而不是像拷贝字符串成员那样拷贝对象的地址),但这贴引出的问题则推翻了这个说法……
作者:
link006007
时间:
2009-9-18 23:45
本帖最后由 link006007 于 2009-9-19 00:15 编辑
..是没有常量池... ...
我误解了不知道有多久呢... ...
我记得Ruby在定义String的结构体时,有一个shared字段... 还以为那是常量池...
不过ls知道... 为什么str.clone仅仅只是引用么?
a = "hello"
b = a.clone
p a.object_id, b.object_id, "hello".object_id, "hello".clone.object_id
复制代码
这里的都不一样- -
到是和Viewport的Rect挺像...
作者:
紫苏
时间:
2009-9-19 00:18
本帖最后由 紫苏 于 2009-9-19 00:39 编辑
str.clone 之后就不是克隆引用,而是克隆值了啊~
你之前克隆的是一个包含了 String 类型成员变量的类实例,浅层拷贝后拷贝的是 str 的引用,所以当修改这个 str 本身的时候,就会反映到所有这个对象的引用那里;str.clone 后当然就是真实的克隆体了(实际上深层拷贝就是递归去调用所有成员的 clone 方法)
s1 = "hello"
s2 = s1.clone
s2 << " world"
p s1, s2
复制代码
那四个对象,第一个是 a,第二个是 a 的克隆,第三个又是一个新的字符串对象了(尽管其内容和前面完全一样,但解释器一旦发现引号就会分配对象),第四个又是克隆,自然都不一样……
虽然 Ruby 的浮点数和长整数类型的实例也不具有唯一性,但直接浅层拷贝也可以,因为这些对象本身都是常量,是不可修改的,这就和 Java 的 String 一样了。即便内存中的常量存储区中有多份相同的拷贝,只要随便指向其中一个就行了,因为程序只需要它的值,而不关心他在内存中算老几,哦呵呵呵呵~
关于深层克隆,有兴趣可以参考:
http://rpg.blue/viewthread.php?tid=131787&highlight=clone
作者:
link006007
时间:
2009-9-19 00:27
哦 我看错了
那个Klass是s1.clone 不是 s1.str.clone = =
我已开始就没搞对 T_T
作者:
IamI
时间:
2009-9-19 08:42
总结结论就是……EB干了坏事吗囧
还是觉得就是临时对象在捣鬼= =
作者:
奶油Da蛋糕
时间:
2009-9-19 08:56
讨论的问题有点深奥=.=看不懂,飘过。=.=
作者:
沉影不器
时间:
2009-9-19 23:10
提示:
作者被禁止或删除 内容自动屏蔽
作者:
紫苏
时间:
2009-9-20 21:09
指针存储在栈上,拷贝的时候直接拷贝栈上的值,结果就是拷贝了指针指向的对象在堆中的地址,这就好像在 Java 中浅层克隆对象时,克隆了栈上的引用一样~
C 扩展的 RGSS 类,也就只有 Font 的几个类变量是 Ruby 变量了……
作者:
沉影不器
时间:
2009-9-20 22:02
提示:
作者被禁止或删除 内容自动屏蔽
作者:
紫苏
时间:
2009-9-20 22:06
本帖最后由 紫苏 于 2009-9-20 22:13 编辑
当然:
p Rect.instance_variables
复制代码
10:11 编辑:
上面写错了,应该是——
p Rect.new.instance_variables
复制代码
作者:
沉影不器
时间:
2009-9-27 21:11
提示:
作者被禁止或删除 内容自动屏蔽
欢迎光临 Project1 (https://rpg.blue/)
Powered by Discuz! X3.1