2012-01-01 24 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