我真的不明白浅版和深版之间的区别。当我测试它时,Ruby的#dup
似乎会创建一个深层副本。
文档说:
Produces a shallow copy of obj---the instance variables of obj are
copied, but not the objects they reference.
但是当我测试它时,它似乎改变了它们引用的对象。
class Klass
attr_accessor :name
end
a = Klass.new
a.name = "John"
b = a.dup
b.name = "Sue"
puts a.name # John
当@name
是objects they reference
之一时,为什么浅层副本就足够了?
什么是最简单的示例,其中需要深层复制?
答案 0 :(得分:1)
试试这个:
class Klass
attr_accessor :name
end
a = Klass.new
a.name = Klass.new #object inside object
a.name.name = 'George'
b = a.dup
puts b.name.name # George
b.name.name = 'Alex'
puts a.name.name # Alex
另请注意(see info):
使用dup时,对象已扩展的所有模块 不会被复制。
编辑:关于字符串的注释(这很有趣) 引用的字符串不会在原始方案中复制。通过这种情况证明了这一点:
a.name = 'George'
puts a.name.object_id # 69918291262760
b = a.dup
puts b.name # George
puts b.name.object_id # 69918291262760
b.name.concat ' likes tomatoes' # append to existing string
puts b.name.object_id # 69918291262760
puts a.name # George likes tomatoes
这可以按预期工作。不会复制引用的对象(包括字符串),并将共享引用。
那么为什么原始例子不会出现呢?这是因为当你将b.name设置为不同的东西时,你将它设置为一个新的字符串。
a.name = 'hello'
对此非常简短:
a.name = String.new('hello')
因此在原始示例中,a.name& b.name不再引用同一个对象,可以查看object_id来查看。
请注意,Fixnum,float,true,false或symbol不是这种情况。这些对象以浅拷贝复制。
答案 1 :(得分:1)
您显示的示例没有描述深拷贝和浅拷贝之间的区别。相反,请考虑以下示例:
class Klass
attr_accessor :name
end
anna = Klass.new
anna.name = 'Anna'
anna_lisa = anna.dup
anna_lisa.name << ' Lisa'
# => "Anna Lisa"
anna.name
# => "Anna Lisa"
通常,dup
和clone
都只是复制您调用方法的实际对象。没有其他引用的对象,如上例中的name
字符串是重复的。因此,在复制之后,原始对象和复制对象都指向同一个名称字符串。
对于deep_dup
,通常所有(相关的)引用对象也是重复的,通常是无限深度。由于这对于所有可能的对象引用来说都很难实现,因此通常人们依赖于特定对象(如哈希和数组)的实现。
相当通用的深层复制的常见解决方法是使用Ruby的Marshal类来序列化对象图并直接再次反序列化它。
anna_lena = Marshal.load( Marshal.dump(anna))
这会创建新对象,实际上是一个deep_dup。由于大多数对象立即支持编组,因此这是一种相当强大的机制。请注意,您应该永远不要解组(即load
)用户提供的数据,因为这会导致远程执行代码漏洞。