Присвоение объектов и указатели - PullRequest
6 голосов
/ 14 апреля 2010

Я немного озадачен назначением объектов и указателями в 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

Я предполагал, что когда я назначу бар бинам, он создаст копию объекта, и изменение одного не повлияет на другое. Увы, вывод показывает иначе.

^_^[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

Это поражает меня. Вызов pop для foo также влияет на baz, так что это не копия, но конкатенация чего-либо на foo влияет только на foo, а не на baz. Так, когда я имею дело с оригинальным объектом, и когда я имею дело с копией? В моих собственных классах, как я могу убедиться, что назначение копирует, а не делает указатели? Помогите этому растерянному парню.

Ответы [ 4 ]

11 голосов
/ 14 апреля 2010

В этом вопросе много вопросов. Главное, что нужно знать, это присвоение , которое никогда не копирует в ruby, но методы часто возвращают новые объекты вместо того, чтобы изменять существующие объекты . Для неизменяемых объектов, таких как Fixnums, вы можете игнорировать это, но для таких объектов, как массивы или экземпляры Foo, чтобы сделать копию, вы должны сделать bar.dup.

Что касается примера массива, foo += не объединяется с массивом, хранящимся в foo, для этого вы должны сделать foo.concat(['a']). Вместо этого он создает новый массив и присваивает ему значение foo. В документации по классу Array упоминается, какие методы изменяют массив на месте, а какие возвращают новый массив.

3 голосов
/ 14 апреля 2010

+ и - в Array каждый возврат новые массивы заполнены соответствующим содержимым, поэтому foo += [...] не влияет на baz нормально. Попробуйте оператор << на foo, и результат будет baz, видя то же самое изменение.

Я не уверен, как Руби обрабатывает другую вещь внутренне, но вы можете попробовать использовать one.clone и two.clone в Foo#initialize.

2 голосов
/ 08 ноября 2012

Вы никогда не имеете дело с копией. Это тот же объект в памяти, но вы просто объявляете 2 ссылки на него: в вашем первом примере: bar и bean указывают на один и тот же объект в памяти; и во втором примере: foo и baz изначально указывают на один и тот же массив в памяти.

Посмотрите на 2 рисунка / рисунка на странице учебника по Java, где объясняется механизм (он такой же, как в Ruby), и только на эти 2 рисунка, которые стоят больше, чем любое объяснение словами: http://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html

0 голосов
/ 13 февраля 2012

Обычно ruby ​​использует указатель ссылки, поэтому когда вы меняете что-то одно, это также влияет.

...