回到Ruby系列文章
Ruby对象复制
对象复制:dup()、clone()
Ruby中可以使用Object#dup()
或Object#clone()
来浅拷贝对象,它们基本等价,区别稍后再谈。下面是使用dup()复制一个对象的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class C attr_accessor :name, :age, :create_time
def initialize(name, age) @name, @age = name, age @create_time = Time.now end end
c = C.new("junmajinlong", 24) sleep 1.5 c1 = c.dup p c1.name p c.create_time p c1.create_time
|
上面使用dup()复制对象c得到对象c1,c和c1两个对象除了少数几个属性,其它状态和属性是一样的,比如实例变量的值相同,具有的实例方法相同,方法的可见性规则相同(即public/private/protected),等等。
dup()和clone()有几点不同:

initialize_dup、initialize_clone和initialize_copy的区别
无论是dup()还是clone(),它们在处理了一些逻辑后,会分别调用initialize_dup()
和initialize_clone()
。
dup()和clone()的伪代码大概是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Object def clone clone = self.class.allocate
clone.copy_instance_variables(self) clone.copy_singleton_class(self)
clone.initialize_clone(self) clone.freeze if frozen?
clone end
def dup dup = self.class.allocate dup.copy_instance_variables(self) dup.initialize_dup(self) dup end end
|
initialize_dup()
和initialize_clone()
在内部又会继续调用initialize_copy()
,在Rubinus的core/kernel.rb中有如下代码,可知它们的关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def initialize_clone(other) initialize_copy(other) end private :initialize_clone
def initialize_dup(other) initialize_copy(other) end private :initialize_dup
def initialize_copy(other) end private :initialize_copy
|
所以,如果自己手动重写initialize_dup
和initialize_clone
时,建议也调用一下initialize_copy
,或者直接super一下(其实就是在调用initialize_copy)。
那么重写这三个方法有什么用呢?如果在复制对象的时候,有些属性不想复制,比如随机数、时间点、ID等各对象独立的属性,那么可以重写这些方法。
要注意:
- 在重写这些方法的时候,这些方法是在复制并填充完对象属性后再调用的,所以在这些方法中只需要修改那些需要修改的属性即可
- 这三个方法内的self指向的是拷贝出的对象,它们还接受一个参数other,other是被复制的源参照对象,例如
c1 = c.dup
,那么other是源对象c,这三个方法内的self是新对象c1
例如,不想在复制对象的时候连对象的@create_time
时间点也复制,而是定义为复制时的时间点。直接重写initialize_copy
即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class C attr_accessor :name, :age, :create_time
def initialize(name, age) @name, @age = name, age @create_time = Time.now end def initialize_copy(other) @create_time = Time.now end end
c = C.new("junmajinlong", 24) sleep 2 c1 = c.dup p c1.name p c.create_time p c1.create_time
|