ruby中的复制 dup clone

Ruby内置的方法Object#clone和Object#dup可以用来copy一个对象,两者区别是dup只复制对象的内容,而clone还复制与对象相关联的内容,如singleton method
[ruby] view plaincopyprint?
s = "cat"
def s.upcase
"CaT"
end
s_dup = s.dup
s_clone = s.clone
s_dup.upcase #=> "CAT" (singleton method not copied)
s_clone.upcase #=> "CaT" (uses singleton method)

dup和clone都是浅复制Shallow Copy,也就是只能复制接受方的内容,而如果接受方包含到其他对象的引用,那么就只是会复制这些引用了。
[ruby] view plaincopyprint?
arr1 = [ 1, "flipper", 3 ]
arr2 = arr1.dup
arr2[2] = 99
arr2[1][2] = ‘a‘
arr1 #=> [1, "flapper", 3]
arr2 #=> [1, "flapper", 99]
可以看到arr1中有一个到String对象的引用,从而arr2也复制了这个引用,当arr2中修改这个引用时,arr1中的也会发生变化。
如果要进行深复制Deep Copy,可以聪明的采用Marshal模块
[ruby] view plaincopyprint?
arr1 = [ 1, "flipper", 3 ]
arr2 = Marshal.load(Marshal.dump(arr1))
arr2[2] = 99
arr2[1][2] = ‘a‘
arr1 #=> [1, "flipper", 3]
arr2 #=> [1, "flapper", 99]
现在就会发现arr2中对String对象的修改不会导致arr1的变化了,因为深了。。。不过Marshal模块并不能把所有的对象都序列化
在class中还有一个与对象复制相关的特殊方法initialize_copy,这个方法会在信息复制完成后执行,看下面这个示例
[ruby] view plaincopyprint?
class Document
attr_accessor :title, :text
attr_reader :timestamp

def initialize(title, text)
@title, @text = title, text
@timestamp = Time.now
end
end

doc1 = Document.new("Random Stuff", "Haha")
sleep 10
doc2 = doc1.clone

doc1.timestamp == doc2.timestamp #=> true
也就是两个对象是完全一样的,构造函数initialize被跳过了,所以两个对象的时间戮timestamp是相同的。如果要采用执行复制操作时的时间,我们可以通过给Document类添加initialize_copy方法来实现。initialize_copy让程序员能完全控制对象复制的状态
[ruby] view plaincopyprint?
class Document #Reopen the class
def initialize_copy(other)
@timestamp = Time.now
end
end

doc3 = Document.new("More Stuff", "Haha")
sleep 10
doc4 = doc1.clone

doc3.timestamp == doc4.timestamp #=> false
再次感慨Ruby的魅力。。。
PS:以上内容主要来自The Ruby Way

用Ruby复制一个对象(object)也许没有你想像的那么容易. 今天我google了半天, 做个总结吧.
先从最简单的开始, b = a 是复制吗? 看代码说话:
>> a= [0,[1,2]]
>> b=a
>> b[0]=88
>> b[1][0]=99
>> b
=> [88, [99, 2]]
>> a
=> [88, [99, 2]]
从上面代码发现, 一但修改b, 原来的a也同时被改变了. 甚至:

>> b.equal?(a)
=> true
原来b跟a根本就是同一个object, 只是马甲不一样罢了. 所以b = a不是复制.
那 b = a.dup呢?? 还是看代码:
>> a= [0,[1,2]]
>> b=a.dup
>> b[0]=88
>> b[1][0]=99
>> b
=> [88, [99, 2]]
>> a
=> [0, [99, 2]]
情况似乎有所好转, 在修改b后, a还是有一部分被修改了.(0没有变,但原来的1变成了99).
所以dup有时候是复制(如在Array只有一级时), 但有时不是复制哦.
再来一个, b = a.clone呢? 上代码:
>> a= [0,[1,2]]
>> b=a.clone
>> b[0]=88
>> b[1][0]=99
>> b
=> [88, [99, 2]]
>> a
=> [0, [99, 2]]
情况几乎跟dup一模一样. 所以clone也不一定可以相信哦!
原来ruby中的dup和clone都是shallow复制, 只针对object的第一级属性.
汗, 难道在Ruby中没有办法复制对像吗? 也不完全是, 看这个:
>> a= [0,[1,2]]
>> b=Marshal.load(Marshal.dump(a))
>> b[0]=88
>> b[1][0]=99
>> b
=> [88, [99, 2]]
>> a= [0,[1,2]]
=> [0, [1, 2]]
修改b后a没有被改变!!! 似乎终于成功找到复制的办法了!!!
为什么要加"似乎"呢? 因为有些object是不能被Marshal.dump的.如:
>> t=Object.new
>> def t.test; puts ‘test’ end
>> Marshal.dump(t)
TypeError: singleton can’t be dumped
from (irb):59:in `dump’
from (irb):59
更完善的复制方案可以考虑给ruby增加一个deep clone功能, 可以参考以下链接:
http://d.hatena.ne.jp/pegacorn/20070417/1176817721

原文地址:https://www.cnblogs.com/qinyan20/p/9674004.html

时间: 2024-10-02 08:03:13

ruby中的复制 dup clone的相关文章

【转】JavaScript中的对象复制(Object Clone)

JavaScript中并没有直接提供对象复制(Object Clone)的方法.因此下面的代码中改变对象b的时候,也就改变了对象a. a = {k1:1, k2:2, k3:3}; b = a; b.k2 = 4; 如果只想改变b而保持a不变,就需要对对象a进行复制. 用jQuery进行对象复制 在可以使用jQuery的情况下,jQuery自带的extend方法可以用来实现对象的复制. a = {k1:1, k2:2, k3:3}; b = {}; $.extend(b,a); 自定义clone

JavaScript中的对象复制(Object Clone)

JavaScript中并没有直接提供对象复制(Object Clone)的方法.因此下面的代码中改变对象b的时候,也就改变了对象a. a = {k1:1, k2:2, k3:3};b = a;b.k2 = 4; 如果只想改变b而保持a不变,就需要对对象a进行复制. 用jQuery进行对象复制 在可以使用jQuery的情况下,jQuery自带的extend方法可以用来实现对象的复制. a = {k1:1, k2:2, k3:3};b = {};$.extend(b,a); 自定义clone()方法

.NET中深复制与浅复制

概述: 在.NET里面,提供对象复制的功能,前提是实现ICloneable接口.ICloneable接口中有一个Clone方法, 可以在类中覆写实现自定义的拷贝方法.对象复制的实现方法有两种:深复制和浅复制. 深复制和浅复制: 浅复制,浅复制是指源对象与复制出来的对象共用一份实体,对其中任何一个对象的改动都会影响另外一个对象.相当于复制了指针. 深复制:指源对象与复制对象互相独立,为新对象重新分配了一段内存空间,并复制源对象的内容.其中任何一个对象的改动都不会对另外一个对象造成影响. 深浅复制与

对象的复制(clone、序列化)

那就先看是clone方法复制对象  摘自 java.lang.Object.clone()分析 首先,看一下源码:public class Object  {    protected native Object clone() throws CloneNotSupportedException;} 由源代码我们会发现: 第一:Object类的clone()方法是一个native方法,native方法的效率一般来说都是远高于Java中的 非native方法.这也解释了为什么要用Object中cl

[转载]Ruby 中$开头的全局变量、内部变量、隐藏变量介绍

转自:http://www.jb51.net/article/48802.htm Ruby 中充满了一系列的隐藏变量,我们可以从这些预定义的全局变量中获取一些有意思的信息. 全局进程变量 $$ 表示当前运行的 ruby 进程. >> $$=> 17170 我们可以从当前进程杀死它自己 >> `kill -9 #{$$}`[1]    17170 killed     irb $? 表示最近一个子进程的状态 >> `echo hello`=> "he

C++中实现对象的clone()

在C#中,许多对象自动实现了clone函数,在C++中,要拷贝一个对象,除了自定义一个拷贝构造函数来实现对象复制外,还可以像C#中那样实现一个clone函数,这需要借助编译器实现的一个隐藏拷贝构造函数,这样的做法,更省心. #include "stdafx.h" #include <iostream> class CA { public: int value; CA* clone() const { return new CA( *this );} //仅一个构造函数 CA

torch中的copy()和clone()

1.torch中的copy()和clone() y = torch.Tensor(2,2):copy(x) ---1 修改y并不改变原来的x y = x:clone()1 修改y也不改变x y = x1 修改y这个时候就开始改变x了 注意,官网中Returns a clone of a tensor. The memory is copied.内存被复制,也即重新分配了一份内存个y,所以y改变x不改变,对于copy是一样的.copy允许复制的维度不一样,但是元素个数必须是相同的. 对于Pytho

谈谈Ruby中的类变量

Ruby中的类变量,很多文章都是不太建议使用的,主要原因在于他的一些特性容易导致犯一些错误,尤其在广泛使用元编程的时候. 初步接触类变量可能觉得他跟C++的类静态成员和Java中的静态变量没什么区别,但在实际使用中一不留神就会掉到类变量的陷阱中去 陷阱1,类变量跟类实例变量不同,类实例变量关联的是self,但类变量关联的是当前类作用域 class C end class D end class C @@var = "C" def D.getvar @@var end end class

(补充1)Ruby中的p、puts、print对比

p 和 puts 是 Ruby 中特别常用的方法,很多童鞋可能认为它们是差不多的,使用的时候也不加注意,但是仔细考究起来,它们是有明显差别的. 先举一个例子: class Foo def inspect "foo from inspect" end def to_s "foo from to_s" endendfoo = Foo.newp fooputs foop "p: <#{foo}>"puts "puts: <#