Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лаб5.doc
Скачиваний:
17
Добавлен:
17.11.2019
Размер:
178.18 Кб
Скачать

Операции

Объекту может быть присвоено значение при помощи одного из двух способов:

  • Явное присваивание в выражении

  • Инициализация

Явное присваивание в выражении

Object A;

Object B;

A = B; // транслируется как Object::operator=(const Object&), таким образом вызывается A.operator=(B)

Инициализация

Объект может быть инициализирован любым из следующих способов.

a. При помощи объявления

Object B = A; // транслируется как Object::Object(const Object&)

b. При помощи аргументов функции

type function (Object a);

c. При помощи возвращаемого значения функции

Object a = function();

Конструктор копирования используется только в последнем случае (инициализации) и не используется вместо присваивания (т.е. там, где используется оператор присваивания).

Неявный конструктор копирования класса вызывает базовые конструкторы копирования и копии их членов, соответствующие их типу. Если это тип класса, то вызывается конструктор копирования. Если это скалярный тип, то используется встроенный оператор присваивания. И наконец, если это массив, то каждый элемент копируется соответствующим их типу образом.[2]

Применением явного конструктора копирования программист может определить дальнейшие действия после копирования объекта.

Примеры

Следующие примеры иллюстрируют как работают конструкторы копирования и почему они иногда требуются.

Неявный конструктор копирования

Рассмотрим следующий пример.

#include <iostream>

class Person

{

public:

int age;

Person(int age) : age(age) {}

};

int main()

{ Person timmy(10);

Person sally(15);

Person timmy_clone = timmy;

std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl;

timmy.age = 23;

std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl;}

Результат

10 15 10

23 15 10

Как и ожидалось, timmy скопировался в новый объект timmy_clone. При изменении возраста (age) timmy, у timmy_clone возраст не менялся. Это потому, что они являются полностью независимыми объектами.

Компилятор сгенерировал для нас конструктор копирования, который может быть записан примерно так:

Person(Person const& copy)

: age(copy.age) {}

Так когда же нам реально требуется явный конструктор копирования? В следующем подразделе рассмотрим этот вопрос.

Явный конструктор копирования

Теперь рассмотрим очень простой класс динамических массивов, как например, нижеследующий:

#include <iostream>

class Array

{ public:

int size;

int* data;

Array(int size)

: size(size), data(new int[size]) {}

~Array()

{ delete[] data;

}

};

int main()

{ Array first(20);

first.data[0] = 25;

{

Array copy = first;

std::cout << first.data[0] << " " << copy.data[0] << std::endl;

} // (1)

first.data[0] = 10; // (2)}

Результат

25 25

Segmentation fault

Хотя мы не указывали конструктор копирования, компилятор сгенерировал его для нас. Генерируемый конструктор выглядит примерно так:

Array(Array const& copy)

: size(copy.size), data(copy.data) {}

Проблема, связанная с этим конструктором, заключается в том, что он выполняет простое копирование указателя data. Он только копирует адрес, а не сами данные. И когда программа доходит до строчки (1), вызывается деструктор copy (объекты в стеке уничтожаются автоматически при достижении их границ). Как видно, деструктор Array удаляет массив data, поэтому когда он удаляет данные copy, он также удаляет данные first. Строка (2)теперь получает неправильные данные и записывает их! Это и приводит к знаменитой ошибке сегментации (segmentation fault).

Если напишем наш собственный конструктор копирования, выполняющий глубокое копирование, то этой проблемы не возникнет.

Array(Array const& copy)

: size(copy.size), data(new int[copy.size])

{ std::copy(copy.data, copy.data + copy.size, data); // #include <algorithm> для std::copy

}

Здесь мы создаем новый массив int и копируем содержимое в него. Теперь, деструктор copy только удалит его данные и не тронет данные first. Строка (2) больше не вызывает ошибку сегментации.

Вместо выполнения глубокого копирования можно использовать несколько оптимизирующих стратегий. Это позволит вам безопасным способом разрешить доступ к данным для нескольких объектов, тем самым экономя память. Стратегия копирование при записи создает копию данных только когда их записывает. Счетчик ссылок содержит счетчик количества объектов ссылающихся на данные и удаляет его только тогда, когда счетчик доходит до нуля (например, boost::shared_ptr).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]