2012-01-01 5 views
6

私はクラスのオブジェクトを持っていますので、それをdupと複製したいと思います。インスタンス変数の1つは配列であり、それを参照しているようです。私はdupが実際に重複を作成したと思った。 dupを参照しない、全く新しい変数を作成するため、インスタンス変数は 'dup'の後でも参照されています

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が変わらないことを期待する:

は、ここに私のIRBセッションです。

[1,2,3]G::initializeのスカラーに置き換える場合、dupはそれを参照しません。

答えて

6

dup crates a shallow copy;インスタンス変数によって参照されるオブジェクトはコピーされません。

正規の(例:Really Easy)ディープコピーハックはマーシャル/アンマーシャルです。これは実際のユースケースでは機能しない場合があります(これは単純な例です)。そうでない場合、またはマーシャリングが効率的でない場合は、initialize_copyルートが最適です。あなたが同じ配列を参照する2つのオブジェクトを持つことになりますので、

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のデフォルトの実装は、ちょうど、浅いコピーを作成します。あなたが望む行動を取得するには、(dupcloneによって呼び出される)initialize_copy関数を定義する必要があります。

class G 
    attr_accessor :iv 
    def initialize_copy(source) 
    super 
    @iv = source.iv.dup 
    end 
end 

次に、2つのオブジェクトは、二つの異なる配列を参照します。配列は、それらに変更可能なオブジェクトを持っている場合は、配列内の各オブジェクトをより深く行くとdupする場合があります

def initialize_copy(source) 
    super 
    @iv = source.iv.collect &:dup 
end 
0

オーバーライドdupまたはclone方法:

def dup 
    Marshal::load(Marshal.dump(self)) 
    end 
関連する問題