2012-01-01 23 views
6

我有一个类的对象,我想用dup复制它。其中一个实例变量是一个数组,它似乎是引用它。我认为dup实际上创建了一个DUPLICATE。实例变量在'dup'后仍然引用

这里是我的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创建一个全新的变量,而不是引用。

另请注意,如果您要用G::initialize中的标量替换[1,2,3],则dup将不会引用它。

回答

6

dup crates a shallow copy;实例变量引用的对象不会被复制。

规范(例如,真正简单)深拷贝黑客是编组/解组,这可能会或可能不会在您的实际用例中工作(假设这是一个简化示例)。如果没有,或者编组效率低下,那么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 
7

dupclone默认的实现只是做一个浅拷贝,所以你将有两个对象是指相同的阵列。为了得到你想要的行为,你应该定义一个initialize_copy功能(这是由dupclone称呼):

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 
0

覆盖dupclone方法:

def dup 
    Marshal::load(Marshal.dump(self)) 
    end