Переменная экземпляра по-прежнему ссылается после 'dup' - PullRequest
6 голосов
/ 01 января 2012

У меня есть объект класса, и я хочу продублировать его с 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::initialize, dup не будет ссылаться на него.

Ответы [ 3 ]

7 голосов
/ 01 января 2012

Реализация по умолчанию dup и clone просто делает поверхностную копию, поэтому у вас будет два объекта, ссылающихся на один и тот же массив. Чтобы получить желаемое поведение, вы должны определить функцию initialize_copy (которая вызывается dup и 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
6 голосов
/ 01 января 2012

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
0 голосов
/ 03 мая 2016

Переопределить dup или clone метод:

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