8

我有一个类的对象,我想用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]为 in 中的标量G::initializedup则不会引用它。

4

3 回答 3

7

dup的默认实现clone只是做一个浅拷贝,所以你将有两个对象引用同一个数组。要获得您想要的行为,您应该定义一个initialize_copy函数(由dupand调用clone):

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
于 2012-01-01T02:14:56.783 回答
6

dup创建一个浅拷贝;实例变量引用的对象不会被复制。

规范的(例如,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
于 2012-01-01T02:09:01.930 回答
0

覆盖dupclone方法:

  def dup
    Marshal::load(Marshal.dump(self))
  end
于 2016-05-03T19:17:14.320 回答