Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

18.2.2. Копирующее присваивание

Копирование векторов может возникать не только при их инициализации, но и при присваивании. Как и при инициализации, по умолчанию копирование производится поэлементно, так что вновь может возникнуть двойное удаление (см. раздел 18.2.1) и утечка памяти. Рассмотрим пример.

void f2(int n)

{

vector v(3); // определяем вектор

v.set(2,2.2);

vector v2(4);

v2 = v; // присваивание: что здесь происходит?

// ...

}

Мы хотели бы, чтобы вектор

v2
был копией вектора
v
(именно так функционирует стандартный класс
vector
), но поскольку в нашем классе
vector
смысл копирования не определен, используется присваивание по умолчанию; иначе говоря, присваивание выполняется почленно, и члены
sz
и
elem
объекта
v2
становятся идентичными элементам
sz
и
elem
объекта
v
соответственно.

Эту ситуацию можно проиллюстрировать следующим образом:

При выходе из функции

f2
возникнет такая же катастрофа, как и при выходе из функции
f
в разделе 18.2, до того, как мы определили копирующий конструктор: элементы, на которые ссылаются оба вектора,
v
и
v2
, будут удалены дважды (с помощью оператора
delete[]
). Кроме того, возникнет утечка памяти, первоначально выделенной для вектора
v2
, состоящего из четырех элементов. Мы “забыли” их удалить. Решение этой проблемы в принципе не отличается от решения задачи копирующей инициализации (см. раздел 18.2.1). Определим копирующий оператор присваивания.

class vector {

int sz;

double* elem;

void copy(const vector& arg); // копирует элементы из arg

// в *elem

public:

vector& operator=(const vector&) ; // копирующее присваивание

// ...

};

vector& vector::operator=(const vector& a)

// делает этот вектор копией вектора a

{

double* p = new double[a.sz]; // выделяем новую память

for (int=0; i<asz; ++i)

p[i]=a.elem[i]; // копируем элементы

delete[] elem; // освобождаем память

elem = p; // теперь можно обновить elem

sz = a.sz;

return *this; // возвращаем ссылку

// на текущий объект
(см. раздел 17.10)

}

Присваивание немного сложнее, чем создание, поскольку мы должны работать со старыми элементами. Наша основная стратегия состоит в копировании элементов из источника класса

vector
.

double* p = new double[a.sz]; // выделяем новую память

for(int=0; i<asz; ++i) p[i]=a.elem[i];

Теперь освобождаем старые элементы из целевого объекта класса

vector
.

delete[] elem; // освобождаем занятую память

В заключение установим указатель

elem
на новые элементы.

elem = p; // теперь можем изменить указатель elem

sz = a.sz;

Теперь в классе

vector
утечка памяти устранена, а память освобождается только один раз (
delete[]
).

Реализуя присваивание, код можно упростить, освобождая память, занятую старыми элементами, до создания копии, но обычно не стоит стирать информацию, если вы не уверены, что ее можно заменить. Кроме того, если вы это сделаете, то при попытке присвоить объект класса vector самому себе могут возникнуть странные вещи.

vector v(10);

v=v; // самоприсваивание

Пожалуйста, убедитесь, что наша реализация функционирует правильно (если не оптимально).

18.2.3. Терминология, связанная с копированием

Копирование встречается в большинстве программ и языков программирования. Основная проблема при этом заключается в том, что именно копируется: указатель (или ссылка) или информация, на которую он ссылается.

  • Читать дальше
  • 1
  • ...
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: