Читаем Программирование полностью

При выходе из функции 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

    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

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

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

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

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

sz = a.sz;

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

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

vector v(10);

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

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

<p id="AutBody_Root325"><strong>18.2.3. Терминология, связанная с копированием</strong></p>

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

Поверхностное копирование (shallow copy) предусматривает копирование только указателя, поэтому в результате на один и тот же объект могут ссылаться два указателя. Именно этот механизм копирования лежит в основе работы указателей и ссылок.

Глубокое копирование (deep copy) предусматривает копирование информации, на которую ссылается указатель, так что в результате два указателя ссылаются на разные объекты. На основе этого механизма копирования реализованы классы vector, string и т.д. Если мы хотим реализовать глубокое копирование, то должны реализовать в наших классах конструктор копирования и копирующее присваивание.

Рассмотрим пример поверхностного копирования.

int* p = new int(77);

int* q = p; // копируем указатель p

*p = 88;    // изменяем значение переменной int, на которую

            // ссылаются указатели p и q 

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

В противоположность этому мы можем осуществить глубокое копирование.

int* p = new int(77);

int* q = new int(*p); // размещаем новую переменную int,

                      // затем копируем значение, на которое

                      // ссылается p

*p = 88;              // изменяем значение, на которое ссылается p

Эту ситуацию можно проиллюстрировать так.

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже