オブジェクトのコピーを作る

スポンサーリンク
スポンサーリンク
ライフスタイル関連のコンテンツ
お金 | 仕事 | 勉強 | プライベート | 健康 |
プログラミング関連のコンテンツ
C言語/C++入門 | Ruby入門 | Python入門 | プログラミング全般

オブジェクトを作る場合、dup, clone メソッドを実行すると、元のオブジェクトからコピーしたオブジェクトが新規に作られます。
dup, clone は、オブジェクトのインスタンス変数をコピーする時に、浅いコピーを行い、値の参照をコピーして実際の値をコピーしません。
したがって、コピー先の変更が、コピー元まで影響する場合があります。
以下、実行例です。(Ruby1.9.1)

スポンサーリンク

class IntAry
  attr_accessor :ints
  def initialize(*ints)
    @ints = ints
  end
end
obj3 = IntAry.new(5, 7, 8, 4, 3)
p obj3.ints   # => [5, 7, 8, 4, 3] 元の配列
obj3_cp = obj3.dup
p obj3.object_id
p obj3_cp.object_id
obj3_cp.ints[1] = 32   # コピーしたオブジェクトの配列を変更
p obj3_cp.ints   # => [5, 32, 8, 4, 3]
p obj3.ints   # => [5, 32, 8, 4, 3] コピー元オブジェクトの配列に影響

実行結果。

[5, 7, 8, 4, 3]
5892740
5892640
[5, 32, 8, 4, 3]
[5, 32, 8, 4, 3]

object_id が違うのでコピーが行われていることが確認できます。
しかし、コピー先で行った「obj3_cp.ints[1] = 32」の変更が、コピー元のオブジェクト obj3 にまで及んでしまっています。
これは、意図した動作じゃないことのほうが多いはず。

initialize_copy メソッドを定義すれば、インスタンス変数である配列のコピーを作成できるようになります。
initialize_copy メソッドがなければ、コピーされたオブジェクトは、オリジナルのオブジェクトと同じ配列を参照することとなり、上記のような実行結果となったわけです。

class IntAry2
  attr_accessor :ints
  def initialize(*ints)
    @ints = ints
  end
  def initialize_copy(orig)
    @ints = @ints.dup # 配列のコピーを作る
  end
end
obj4 = IntAry2.new(5, 7, 8, 4, 3)
p obj4.ints   # => [5, 7, 8, 4, 3] 元の配列
obj4_cp = obj4.dup
p obj4.object_id
p obj4_cp.object_id
obj4_cp.ints[1] = 32   # コピーしたオブジェクトの配列を変更
p obj4_cp.ints   # => [5, 32, 8, 4, 3]
p obj4.ints   # => [5, 7, 8, 4, 3] コピー元オブジェクトの配列に影響がない

実行結果。

[5, 7, 8, 4, 3]
5892230
5892130
[5, 32, 8, 4, 3]
[5, 7, 8, 4, 3]

今度は、コピー元オブジェクトであるobj4は、コピー先オブジェクト変更の影響がありません。
cloneとdupは、オリジナルからオブジェクトをコピーする際に、インスタンス変数をコピーしたあとに、initialize_copy(orig) を呼び出す働きを持つ。

なお、cloneとdupの違いは、以下。

Object – Rubyリファレンスマニュアル

clone
dup

オブジェクトの複製を作成して返します。

clone は freeze、taint、特異メソッドなどの情報も含めた完全な複製を、dup はオブジェクトの内容のみの複製を作ります。

clone や dup は「浅い(shallow)」コピーであることに注意してください。オブジェクト自身を複製するだけで、オブジェクトの指している先(たとえば配列の要素など)までは複製しません。

スポンサーリンク
 
スポンサーリンク