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

时间:2012-01-01 02:04:02

标签: 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]

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

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

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:6)

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

规范(例如,Really Easy)深层复制黑客是编组/解组,这可能会或可能不会在您的实际用例中起作用(假设这是一个简化的示例)。如果没有,或者编组效率低下,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

答案 2 :(得分:0)

覆盖dupclone方法:

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