При выходе из функции 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 v(10);
v=v; // самоприсваивание
Пожалуйста, убедитесь, что наша реализация функционирует правильно (если не оптимально).
18.2.3. Терминология, связанная с копированием
Копирование встречается в большинстве программ и языков программирования. Основная проблема при этом заключается в том, что именно копируется: указатель (или ссылка) или информация, на которую он ссылается.
•
• 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
Эту ситуацию можно проиллюстрировать так.