Книга: Программирование на языке Ruby
11.1.9. Копирование объектов
11.1.9. Копирование объектов
Встроенные методы Object#clone
и #dup
порождают копию вызывающего объекта. Различаются они объемом копируемого контекста. Метод #dup
копирует только само содержимое объекта, тогда как clone
сохраняет и такие вещи, как синглетные классы, ассоциированные с объектом.
s1 = "cat"
def s1.upcase
"CaT"
end
s1_dup = s1.dup
s1_clone = s1.clone
s1 #=> "cat"
s1_dup.upcase #=> "CAT" (синглетный метод не копируется)
s1_clone.upcase #=> "СаТ" (используется синглетный метод)
И dup
, и clone
выполняют поверхностное копирование, то есть копируют лишь содержимое самого вызывающего объекта. Если вызывающий объект содержит ссылки на другие объекты, то последние не копируются — копия будет ссылаться на те же самые объекты. Проиллюстрируем это на примере. Объект arr2
— копия arr1
, поэтому изменение элемента целиком, например arr2[2]
, не оказывает влияния на arr1
. Но исходный массив и его копия содержат ссылку на один и тот же объект String
, поэтому изменение строки через arr2
приведет к такому же изменению значения, на которое ссылается arr1
.
arr1 = [ 1, "flipper", 3 ]
arr2 = arr1.dup
arr2[2] = 99
arr2[1][2] = 'a'
arr1 # [1, "flapper", 3]
arr2 # [1, "flapper", 99]
Иногда необходимо глубокое копирование, при котором копируется все дерево объектов с корнем в исходном объекте. В этом случае между оригиналом и копией гарантированно не будет никакой интерференции. Ruby не предоставляет встроенного метода для глубокого копирования, но есть приемы, позволяющие достичь желаемого результата.
Самый «чистый» способ — потребовать, чтобы классы реализовывали метод deep_copy
. Он мог бы рекурсивно обходить все объекты, на которые ссылается исходный объект, и вызывать для них метод deep_copy
. Необходимо было бы еще добавить метод deep_copy
во все встроенные классы Ruby, которыми вы пользуетесь.
Но есть и более быстрый способ с использованием модуля Marshal
. Если вы сериализуете исходный объект, представив его в виде строки, а затем загрузите в новый объект, то этот новый объект будет копией исходного.
arr1 = [ 1, "flipper", 3 ]
arr2 = Marshal.load(Marshal.dump(arr1))
arr2[2] = 99
arr2[1][2] = 'a'
arr1 # [1, "flipper", 3]
arr2 # [1, "flapper", 99]
Обратите внимание, что изменение строки через arr2
не отразилось на строке, на которую ссылается arr1
.
- 11.1.1. Применение нескольких конструкторов
- 11.1.2. Создание атрибутов экземпляра
- 11.1.3. Более сложные конструкторы
- 11.1.4. Создание атрибутов и методов уровня класса
- 11.1.5. Наследование суперклассу
- 11.1.6. Опрос класса объекта
- 11.1.7. Проверка объектов на равенство
- 11.1.8. Управление доступом к методам
- 11.1.9. Копирование объектов
- 11.1.10. Метод initialize_copy
- 11.1.11. Метод allocate
- 11.1.12. Модули
- 11.1.13. Трансформация или преобразование объектов
- 11.1.14. Классы, содержащие только данные (Struct)
- 11.1.15. Замораживание объектов
- 6.2. Описание объектов
- Перемещение и копирование файлов и папок
- Копирование объектов
- Совет 3. Реализуйте быстрое и корректное копирование объектов в контейнерах
- Резервное копирование базы данных InterBase
- Резервное копирование многофайловых баз данных
- Резервное копирование при работе InterBase в режиме 24x7
- Иерархия объектов в InterBase
- Имена объектов длиной 68 символов
- Создание объектов Collection
- 8.2.8. Копирование хэша в массив
- 3.3. Определение объектов защиты