【问题标题】:Object assignment and pointers对象分配和指针
【发布时间】:2011-02-07 18:35:03
【问题描述】:

我对 Ruby 中的对象分配和指针有点困惑,并编写了这个 sn-p 代码来测试我的假设。

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 分配给 beans 时,它会创建对象的副本,并且修改一个不会影响另一个。唉,输出显示不是这样。

^_^[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。那么我什么时候处理原始对象,什么时候处理副本?在我自己的课程中,我怎样才能确保作业复制,并且不做指针?帮助这个困惑的家伙。

【问题讨论】:

    标签: ruby object copy variable-assignment


    【解决方案1】:

    你永远不会处理副本。它是内存中的同一个对象,但您只需声明对它的 2 个引用:在您的第一个示例中:bar 和 beans 指向内存中的同一个对象;在您的第二个示例中: foo 和 baz 最初指向内存中的同一个数组。

    查看解释机制的 Java 教程页面中的 2 张图片/绘图(它与 Ruby 中的相同)以及仅这 2 张图片,比任何文字解释都更有价值: http://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html

    【讨论】:

      【解决方案2】:

      基本上 ruby​​ 使用引用指针,所以当你改变一件事时,其他的也会受到影响。

      【讨论】:

        【解决方案3】:

        这个问题有很多问题。要知道的主要事情是赋值永远不会在 ruby​​ 中创建副本,但方法通常会返回新对象而不是修改现有对象。对于像 Fixnums 这样的不可变对象,您可以忽略这一点,但对于像数组或 Foo 实例这样的对象,您必须使用 bar.dup 进行复制。

        至于数组示例,foo += 没有连接到存储在foo 中的数组上,要做到这一点,您可以使用foo.concat(['a'])。相反,它正在创建一个新数组并将foo 分配给它。 Array 类的文档提到了哪些方法会改变数组,哪些方法会返回一个新数组。

        【讨论】:

        • 那么我将如何在我自己的类中实现 .clone 功能?
        • 在 Foo 中克隆的实现只会返回 Foo.new(self.one, self.two)
        • 在这种情况下,继承的Object#clone 也可以,因为它也会复制实例变量。
        • Object#dup 如果你不希望你的对象被冻结
        【解决方案4】:

        +Array 中的- 各自返回new 数组填充各自的内容,所以foo += [...] 不影响baz 是正常的。在foo 上尝试&lt;&lt; 运算符,结果将是baz 看到相同的变化。

        我不确定 Ruby 如何在内部处理其他事情,但您可以尝试在 Foo#initialize 中使用 one.clonetwo.clone

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-07
          • 1970-01-01
          • 2013-07-08
          • 2021-03-29
          • 2017-12-06
          • 1970-01-01
          相关资源
          最近更新 更多