我对Ruby中的对象赋值和指针感到有些困惑,并编写了这段代码来测试我的假设。
class Foo
attr_accessor :one, :two
def initialize(one, two)
@one = one
@two = two
end
end
bar = Foo.new(1, 2)
beans = bar
puts bar
puts beans
beans.one = 2
puts bar
puts beans
puts beans.one
puts bar.one
我曾经假设当我将bar分配给bean时,它会创建一个对象的副本,而修改一个不会影响另一个。唉,输出显示不然。
^_^[jergason:~]$ ruby test.rb
#<Foo:0x100155c60>
#<Foo:0x100155c60>
#<Foo:0x100155c60>
#<Foo:0x100155c60>
2
2
我认为这些数字与对象的地址有关,并且它们对于bean和bar都是相同的,当我修改bean时,bar也会被更改,这不是我所期望的。看来我只是创建一个指向对象的指针,而不是它的副本。在分配时复制对象而不是创建指针需要做什么?
使用Array类进行的测试也显示了一些奇怪的行为。
foo = [0, 1, 2, 3, 4, 5]
baz = foo
puts "foo is #{foo}"
puts "baz is #{baz}"
foo.pop
puts "foo is #{foo}"
puts "baz is #{baz}"
foo += ["a hill of beans is a wonderful thing"]
puts "foo is #{foo}"
puts "baz is #{baz}"
这会产生以下不稳定的输出:
foo is 012345
baz is 012345
foo is 01234
baz is 01234
foo is 01234a hill of beans is a wonderful thing
baz is 01234
这让我大吃一惊。在foo上调用pop会影响baz,因此它不是副本,但是将某些内容连接到foo上只会影响foo,而不会影响baz。那么我什么时候处理原始对象,何时处理副本?在我自己的课程中,我如何确保分配副本,而不是指针?帮助这个迷茫的家伙。
答案 0 :(得分:11)
这个问题有很多问题。要知道的主要事情是赋值永远不会在ruby中复制,但方法通常返回新对象而不是修改现有对象。对于像Fixnums这样的不可变对象,你可以忽略它,但对于像数组或Foo实例这样的对象,要制作副本,你必须bar.dup
。
对于数组示例,foo +=
未连接到foo
中存储的数组,为此,您需要执行此操作foo.concat(['a'])
。相反,它正在创建一个新数组并为其分配foo
。 Array类的文档提到了哪些方法在适当的位置改变了数组并返回了一个新数组。
答案 1 :(得分:3)
+
中的{p> -
和Array
每个都会返回新的数组,这些数组中包含相应的内容,因此foo += [...]
不会影响baz
是正常的。试试<<
上的foo
运算符,结果将baz
看到相同的变化。
我不确定Ruby如何在内部处理其他内容,但您可以尝试在one.clone
中使用two.clone
和Foo#initialize
。
答案 2 :(得分:2)
你永远不会处理副本。它在内存中是相同的对象,但是你只需要声明它的2个引用:在你的第一个例子中:bar和beans指向内存中的同一个对象;在你的第二个例子中:foo和baz最初指向内存中的相同数组。
查看2图片/图纸,在Java教程页面中解释机制(它与Ruby中的相同)和那些仅仅2张图片,比单词中的任何解释更有价值: http://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html
答案 3 :(得分:0)
基本上ruby正在使用引用指针,所以当你改变一件事时,其他东西也会受到影响。