Шрифт:
alloc.deallocate(elem,space); // освобождаем старую память
elem = p;
space = newalloc;
}
alloc.construct(&p[i],elem[i])
может генерировать исключение. Следовательно, указатель p
— это пример проблемы, о которой мы предупреждали в разделе 19.5.1. Ой! Можно было бы применить класс auto_ptr
. А еще лучше — вернуться назад и понять, что память для вектора — это ресурс; иначе говоря, мы можем определить класс vector_base
для выражения фундаментальной концепции, которую используем все время. Эта концепция изображена на следующем рисунке, содержащем три элемента, определяющих использование памяти, предназначенной для вектора: Добавив для полноты картины распределитель памяти, получим следующий код:
template<class T, class A>
struct vector_base {
A alloc; // распределитель памяти
T* elem; // начало распределения
int sz; // количество элементов
int space; // размер выделенной памяти
vector_base(const A& a, int n)
:alloc(a), elem(a.allocate(n)), sz(n), space(n) { }
~vector_base { alloc.deallocate(elem,space); }
};
Обратите внимание на то, что класс
vector_base
работает с памятью, а не с типизированными объектами. Нашу реализацию класса vector
можно использовать для владения объектом, имеющим желаемый тип элемента. По существу, класс vector
— это просто удобный интерфейс для класса vector_base
.
template<class T, class A = allocator<T> >
class vector:private vector_base<T,A> {
public:
// ...
};
Теперь можно переписать функцию
reserve
, сделав ее более простой и правильной.
template<class T, class A>
void vector<T,A>::reserve(int newalloc)
{
if (newalloc<=space) return; // размер никогда не уменьшается
vector_base<T,A> b(alloc,newalloc); // выделяем новую память
for (int i=0; i<sz; ++i)
alloc.construct(&b.elem[i], elem[i]); // копируем
for (int i=0; i<sz; ++i)
alloc.destroy(&elem[i]); // освобождаем память
swap< vector_base<T,A> >(*this,b); // меняем представления
// местами
}
При выходе из функции
reserve
старая память автоматически освобождается деструктором класса vector_base
, даже если выход был вызван операцией копирования, сгенерировавшей исключение. Функция swap
является стандартным библиотечным алгоритмом (из заголовка <algorithm>
), меняющим два объекта местами. Мы использовали алгоритм swap<vector_base<T,A>>(*this,b)
, а не более простую функцию swap(*this,b)
, поскольку объекты *this
и b
имеют разные типы (vector
и vector_base
соответственно), поэтому должны явно указать, какую специализацию алгоритма swap
следует выполнить. ПОПРОБУЙТЕ
Модифицируйте функцию
reserve
, чтобы она использовала класс auto_ptr
. Помните о необходимости освободить память перед возвратом из функции. Сравните это решение с классом vector_base
. Выясните, какое из них лучше и какое легче реализовать. Задание
1. Определите класс
template<class T> struct S { T val; };
. 2. Добавьте конструктор, чтобы можно было инициализировать его типом
T
. 3. Определите переменные типов
S<int>
, S<char>
, S<double>
, S<string>
и S<vector<int>>
; инициализируйте их значениями по своему выбору. 4. Прочитайте эти значения и выведите их на экран.
5. Добавьте шаблонную функцию
get
, возвращающую ссылку на значение val
. 6. Разместите функцию
get
за пределами класса. 7. Разместите значение
val
в закрытом разделе.