2012-01-01 20 views
6

Tengo un objeto de una clase, y quiero duplicarlo con dup. Una de las variables de instancia es una matriz, y parece estar haciendo referencia a ella. Pensé que dup creó un DUPLICADO.Variable de instancia todavía referencias después de 'dup'

Aquí está mi sesión de 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] 

Yo esperaría a no haber cambiado, porque dup crea una variable nueva, no referencia.

También tenga en cuenta que si reemplazara [1,2,3] con un escalar en G::initialize, dup no lo referenciará.

Respuesta

6

dup crates a shallow copy; los objetos a los que se refieren las variables de instancia no se copian.

El hack de copia profunda canónico (por ejemplo, Really Easy) es ordenar/unmarshal, lo que puede funcionar o no en el uso real (suponiendo que este sea un ejemplo simplificado). Si no es así, o si el marshalling es ineficiente, la ruta initialize_copy es una mejor opción.

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 
7

La implementación predeterminada de dup y clone simplemente hacer una copia superficial, por lo que tendrá dos objetos que se refieren a la misma matriz. Para obtener el comportamiento que desea, debe definir una función initialize_copy (que es llamada por dup y clone):

class G 
    attr_accessor :iv 
    def initialize_copy(source) 
    super 
    @iv = source.iv.dup 
    end 
end 

A continuación, los dos objetos se refieren a dos matrices diferentes. Si las matrices tienen objetos mutables en ellos, es posible que desee ir aún más profundo y dup cada objeto en las matrices:

def initialize_copy(source) 
    super 
    @iv = source.iv.collect &:dup 
end 
0

Anulación dup o clone método:

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