对象分配和指针
我对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 # # # # 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。 那么我什么时候处理原始对象,何时处理副本? 在我自己的课程中,我如何确保分配副本,而不是指针? 帮助这个迷茫的家伙。
这个问题有很多问题。 要知道的主要事情是赋值永远不会在ruby中复制,但方法通常返回新对象而不是修改现有对象 。 对于像Fixnums这样的不可变对象,你可以忽略它,但对于像数组或Foo实例这样的对象,要制作副本你必须做bar.dup
。
至于数组示例, foo +=
没有连接到存储在foo
的数组,为此你要做foo.concat(['a'])
。 相反,它正在创建一个新数组并为其分配foo
。 Array类的文档提到了哪些方法在适当的位置改变了数组并返回了一个新数组。
+
和-
在Array
每个返回填充相应内容的新数组,因此foo += [...]
不影响baz
是正常的。 在foo
上尝试<<
运算符,结果将是baz
看到相同的变化。
我不确定Ruby如何处理内部的其他事情,但你可能会尝试在Foo#initialize
two.clone
中使用one.clone
和two.clone
。
你永远不会处理副本。 它是内存中的同一个对象,但你只需要声明它的2个引用:在你的第一个例子中:bar和beans指向内存中的同一个对象; 在你的第二个例子中:foo和baz最初指向内存中的相同数组。
查看2图片/图纸,在Java教程页面中解释机制(它与Ruby中的相同)和那些仅仅是2张图片,比任何文字解释更有价值: http : //docs.oracle.com/javase /tutorial/java/javaOO/objectcreation.html
基本上ruby是使用引用指针,所以当你改变一件事时,其他的也会受到影响。