我有一个类的对象,我想用dup
复制它。其中一个实例变量是一个数组,它似乎引用了它。我认为dup实际上创建了一个DUPLICATE。
这是我的IRB会议:
irb(main):094:0> class G
irb(main):095:1> attr_accessor :iv
irb(main):096:1> def initialize
irb(main):097:2> @iv = [1,2,3]
irb(main):098:2> end
irb(main):099:1> end
=> nil
irb(main):100:0> a=G.new
=> #<G:0x27331f8 @iv=[1, 2, 3]>
irb(main):101:0> b=a.dup
=> #<G:0x20e4730 @iv=[1, 2, 3]>
irb(main):103:0> b.iv<<4
=> [1, 2, 3, 4]
irb(main):104:0> a
=> #<G:0x27331f8 @iv=[1, 2, 3, 4]
我希望a
保持不变,因为dup
会创建一个全新的变量,而不是引用。
另请注意,如果您要使用[1,2,3]
中的标量替换G::initialize
,dup
将不会引用它。
答案 0 :(得分:7)
dup
和clone
的默认实现只是做一个浅拷贝,所以你将有两个对象引用同一个数组。要获得所需的行为,您应该定义initialize_copy
函数(由dup
和clone
调用):
class G
attr_accessor :iv
def initialize_copy(source)
super
@iv = source.iv.dup
end
end
然后这两个对象将引用两个不同的数组。如果数组中包含可变对象,您可能希望更深入并且dup
数组中的每个对象:
def initialize_copy(source)
super
@iv = source.iv.collect &:dup
end
答案 1 :(得分:6)
dup
crates a shallow copy;实例变量引用的对象不会被复制。
规范(例如,Really Easy)深层复制黑客是编组/解组,这可能会或可能不会在您的实际用例中起作用(假设这是一个简化的示例)。如果没有,或者编组效率低下,initialize_copy
路由是更好的选择。
pry(main)> a = G.new
=> #<G:0x9285628 @iv=[1, 2, 3]>
pry(main)> b = a.dup
=> #<G:0x92510a8 @iv=[1, 2, 3]>
pry(main)> a.iv.__id__
=> 76819210
pry(main)> b.iv.__id__
=> 76819210
pry(main)> b = Marshal::load(Marshal.dump(a))
=> #<G:0x9153c3c @iv=[1, 2, 3]>
pry(main)> a.__id__
=> 76819220
pry(main)> b.__id__
=> 76193310
答案 2 :(得分:0)
覆盖dup
或clone
方法:
def dup
Marshal::load(Marshal.dump(self))
end