dup的Ruby文档说:

通常,clone和dup在后代类中可能具有不同的语义。虽然clone用于复制对象,包括其内部状态,但dup通常使用后代对象的类来创建新实例。

但当我做了一些测试后,我发现它们实际上是一样的:

class Test
   attr_accessor :x
end

x = Test.new
x.x = 7
y = x.dup
z = x.clone
y.x => 7
z.x => 7

那么这两种方法有什么不同呢?


当前回答

子类可以覆盖这些方法以提供不同的语义。在Object本身中,有两个关键的区别。

首先,clone复制单例类,而dup不这样做。

o = Object.new
def o.foo
  42
end

o.dup.foo   # raises NoMethodError
o.clone.foo # returns 42

其次,clone会保留冻结状态,而dup则不会。

class Foo
  attr_accessor :bar
end
o = Foo.new
o.freeze

o.dup.bar = 10   # succeeds
o.clone.bar = 10 # raises RuntimeError

这些方法的Rubinius实现 是我经常得到这些问题答案的来源,因为它非常清晰,而且是一个相当兼容的Ruby实现。

其他回答

当处理ActiveRecord时,也有一个显著的区别:

Dup创建了一个没有设置id的新对象,所以你可以通过点击.save将一个新对象保存到数据库中

category2 = category.dup
#=> #<Category id: nil, name: "Favorites"> 

Clone创建了一个具有相同id的新对象,因此如果点击.save,对该新对象所做的所有更改将覆盖原始记录

category2 = category.clone
#=> #<Category id: 1, name: "Favorites">

子类可以覆盖这些方法以提供不同的语义。在Object本身中,有两个关键的区别。

首先,clone复制单例类,而dup不这样做。

o = Object.new
def o.foo
  42
end

o.dup.foo   # raises NoMethodError
o.clone.foo # returns 42

其次,clone会保留冻结状态,而dup则不会。

class Foo
  attr_accessor :bar
end
o = Foo.new
o.freeze

o.dup.bar = 10   # succeeds
o.clone.bar = 10 # raises RuntimeError

这些方法的Rubinius实现 是我经常得到这些问题答案的来源,因为它非常清晰,而且是一个相当兼容的Ruby实现。

更新的文档包含了一个很好的例子:

class Klass
  attr_accessor :str
end

module Foo
  def foo; 'foo'; end
end

s1 = Klass.new #=> #<Klass:0x401b3a38>
s1.extend(Foo) #=> #<Klass:0x401b3a38>
s1.foo #=> "foo"

s2 = s1.clone #=> #<Klass:0x401b3a38>
s2.foo #=> "foo"

s3 = s1.dup #=> #<Klass:0x401b3a38>
s3.foo #=> NoMethodError: undefined method `foo' for #<Klass:0x401b3a38>

两者几乎完全相同,但克隆比dup多做一件事。在克隆中,对象的冻结状态也会被复制。在dup里,它总是会融化的。

 f = 'Frozen'.freeze
  => "Frozen"
 f.frozen?
  => true 
 f.clone.frozen?
  => true
 f.dup.frozen?
  => false 

一个不同之处在于冷冻物体。冻结对象的克隆也被冻结(而冻结对象的dup则不是)。

class Test
  attr_accessor :x
end
x = Test.new
x.x = 7
x.freeze
y = x.dup
z = x.clone
y.x = 5 => 5
z.x = 5 => TypeError: can't modify frozen object

另一个区别是单例方法。同样的道理,dup不复制,但clone可以。

def x.cool_method
  puts "Goodbye Space!"
end
y = x.dup
z = x.clone
y.cool_method => NoMethodError: undefined method `cool_method'
z.cool_method => Goodbye Space!