'dup'后实例变量仍然引用

itd*_*ork 7 ruby dup

我有一个类的对象,我想用它复制它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]
Run Code Online (Sandbox Code Playgroud)

我希望a不会改变,因为dup创建一个全新的变量,而不是引用.

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

Dav*_*son 7

默认的实现dupclone只是让一个浅拷贝,所以你将有两个对象是指相同的阵列.要获得所需的行为,您应该定义一个initialize_copy函数(由dupand 调用clone):

class G
  attr_accessor :iv
  def initialize_copy(source)
    super
    @iv = source.iv.dup
  end
end
Run Code Online (Sandbox Code Playgroud)

然后这两个对象将引用两个不同的数组.如果数组中包含可变对象,您可能希望更深入地dup了解数组中的每个对象:

def initialize_copy(source)
  super
  @iv = source.iv.collect &:dup
end
Run Code Online (Sandbox Code Playgroud)


Dav*_*ton 6

dup装箱浅滩 ; 不复制实例变量引用的对象.

规范(例如,真正容易)深层复制黑客是编组/解组,这可能会或可能不会在您的实际用例中起作用(假设这是一个简化的示例).如果没有,或者编组效率低,那么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
Run Code Online (Sandbox Code Playgroud)