Согласно общепринятому соглашению оператор присваивания возвращает ссылку на целевой объект. Смысл выражения *this
объяснялся в разделе 17.10. Его реализация является корректной, но, немного поразмыслив, легко увидеть, что мы выполняем избыточные операции выделения и освобождения памяти. Что делать, если целевой вектор содержит больше элементов, чем присваиваемый вектор? Что делать, если целевой вектор содержит столько же элементов, сколько и присваиваемый вектор? Во многих приложениях последняя ситуация встречается чаще всего. В любом случае мы можем просто скопировать элементы в память, уже выделенную ранее целевому вектору.
vector& vector::operator=(const vector& a)
{
if (this==&a) return *this; // самоприсваивание, ничего делать
// не надо
if (a.sz<=space) { // памяти достаточно, новая память
// не нужна
for (int i = 0; i
sz = a.sz;
return *this;
}
double* p = new double[a.sz]; // выделяем новую память
for (int i = 0; i
// элементы
delete[] elem; // освобождаем старую память
space = sz = a.sz; // устанавливаем новый размер
elem = p; // устанавливаем указатель на новые
// элементы
return *this; // возвращаем ссылку на целевой объект
}
В этом фрагменте кода мы сначала проверяем самоприсваивание (например, v=v
); в этом случае ничего делать не надо. С логической точки зрения эта проверка лишняя, но иногда она позволяет значительно оптимизировать программу. Эта проверка демонстрирует использование указателя this
, позволяющего проверить, является ли аргумент a тем же объектом, что и объект, из которого вызывается функция-член (т.е. operator=()
). Убедитесь, что этот код действительно работает, если из него удалить инструкцию this==&a
. Инструкция a.sz<=space
также включена для оптимизации. Убедитесь, что этот код действительно работает после удаления из него инструкции a.sz<=space
.
19.2.6. Предыдущая версия класса vector
Итак, мы получили почти реальный класс vector
для чисел типа double
.
// почти реальный вектор чисел типа double
class vector {
/*
инвариант:
для 0<=n
sz<=space;
если sz
для (space–sz) чисел типа double
*/
int sz; // размер
double* elem; // указатель на элементы (или 0)
int space; // количество элементов плюс количество слотов
public:
vector():sz(0),elem(0),space(0) { }
explicit vector(int s):sz(s),elem(new double[s]),space(s)
{
for (int i=0; i
// инициализированы
}
vector(const vector&); // копирующий конструктор
vector& operator=(const vector&); // копирующее присваивание
~vector() { delete[] elem; } // деструктор
double& operator[ ](int n) { return elem[n]; } // доступ
const double& operator[](int n) const { return elem[n]; }
int size() const { return sz; }
int capacity() const { return space; }
void resize(int newsize); // увеличение
void push_back(double d);
void reserve(int newalloc);
};
Обратите внимание на то, что этот класс содержит все основные операции (см. раздел 18.3): конструктор, конструктор по умолчанию, копирующий конструктор, деструктор. Он также содержит операции для доступа к данным (индексирование []
), получения информации об этих данных (size()
и capacity()
), а также для управления ростом вектора (resize()
, push_back()
и reserve()
).
19.3. Шаблоны
Однако нам мало иметь вектор, состоящий из чисел типа double
; мы хотим свободно задавать тип элементов наших векторов. Рассмотрим пример.
vector
vector
vector
vector