ruby cookbook -- Making a Copy of an Object

[b]Recipe 8.16. Making a Copy of an Object[/b]
[color=darkred]The downside of dup is that it creates a new instance of the object's original class.[/color] If you open up a specific object and give it a singleton method, you implicitly create a metaclass, an anonymous subclass of the original class. Calling dup on the object will yield a copy that lacks the singleton methods. The other object-copy method, Object#clone, makes a copy of the metaclass and instantiates the copy, instead of instantiating the object's original class.

material = 'cotton'
class << material
def definition
puts 'The better half of velour.'
end
end

material.definition
# The better half of velour.

'cotton'.definition
# NoMethodError: undefined method 'definition' for "cotton":String

material.clone.definition
# The better half of velour.

material.dup.definition
# NoMethodError: undefined method 'definition' for "cotton":String

[color=darkred]Object#clone and Object#dup both perform shallow copies[/color]: they make copies of an object without also copying its instance variables. You'll end up with two objects whose instance variables point to the same objects. Modifications to one object's instance variables will be visible in the other object. This can cause problems if you're not expecting it:

class StringHolder
attr_reader :string
def initialize(string)
@string = string
end
end

s1 = StringHolder.new('string')
s2 = s1.dup
s3 = s1.clone

s1.string[1] = 'p'
s2.string # => "spring"
s3.string # => "spring"

[color=darkred]If you want to do a deep copy, an easy (though not particularly quick) way is to serialize the object to a binary string with Marshal, then load a new object from the string:[/color]


class Object
def deep_copy
Marshal.load(Marshal.dump(self))
end
end

s1 = StringHolder.new('string')
s2 = s1.deep_copy
s1.string[1] = 'p'
s1.string # => "spring"
s2.string # => "string"


Note that this will only work on an object that has no singleton methods:

class << s1
def definition
puts "We hold strings so you don't have to."
end
end
s1.deep_copy
# TypeError: singleton can't be dumped


When an object is cloned or duplicated, Ruby creates a new instance of its class or superclass, but without calling the initialize method. If you want to define some code to run when an object is cloned or duplicated, define an initialize_copy method. This is a hook method that gives you a chance to modify the copy before Ruby passes it back to whoever called clone or dup. If you want to simulate a deep copy without using Marshal, this is your chance to modify the copy's instance variables:

class StringHolder
def initialize_copy(from)
@string = from.string.dup
end
end

s1 = StringHolder.new('string')
s2 = s1.dup
s3 = s1.clone
s1.string[1] = "p"
s2.string # => "string"
s3.string # => "string"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值