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

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

Шрифт:

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
в закрытом разделе.

  • Читать дальше
  • 1
  • ...
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • ...

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

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

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

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