Литмир - Электронная Библиотека
A
A

{

  if (newalloc<=space) return;     // размер не уменьшается

  T* p = alloc.allocate(newalloc); // выделяем новую память

  for (int i=0; i<sz; ++i) alloc.construct(&p[i],elem[i]);

                                   // копируем

  for (int i=0; i<sz; ++i) alloc.destroy(&elem[i]); // уничтожаем

  alloc.deallocate(elem,space);    // освобождаем старую память

  elem = p;

  space = newalloc;

}

Мы перемещаем элемент в новый участок памяти, создавая копию в неинициализированной памяти, а затем уничтожая оригинал. Здесь нельзя использовать присваивание, потому что для таких типов, как

string
, присваивание подразумевает, что целевая область памяти уже проинициализирована.

Имея функции

reserve()
,
vector<T,A>::push_back()
, можно без труда написать следующий код.

template<class T, class A>

void vector<T,A>::push_back(const T& val)

{

  if (space==0) reserve(8);        // начинаем с памяти для 8 элементов

  else if (sz==space) reserve(2*space); // выделяем больше памяти

  alloc.construct(&elem[sz],val);  // добавляем в конец

                                   // значение val

  ++sz;                            // увеличиваем размер

}

Аналогично можно написать функцию

vector<T,A>::resize()
.

template<class T, class A>

void vector<T,A>::resize(int newsize, T val = T())

{

  reserve(newsize);

  for (int i=sz; i<newsize; ++i) alloc.construct(&elem[i],val);

  // создаем

  for (int i = newsize; i<sz; ++i) alloc.destroy(&elem[i]);

  // уничтожаем

  sz = newsize;

}

Обратите внимание на то, что, поскольку некоторые типы не имеют конструкторов по умолчанию, мы снова предоставили возможность задавать начальное значение для новых элементов.

Другое новшество — деструктор избыточных элементов при уменьшении вектора. Представьте себе деструктор, превращающий объект определенного типа в простой набор ячеек памяти.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 “Непринужденное обращение с распределителями памяти” — это довольно сложное и хитроумное искусство. Не старайтесь злоупотреблять им, пока не почувствуете, что стали экспертом.

19.4. Проверка диапазона и исключения

 Мы проанализировали текущее состояние нашего класса

vector
и обнаружили (с ужасом?), что в нем не предусмотрена проверка выхода за пределы допустимого диапазона. Реализация оператора
operator[]
не вызывает затруднений.

template<class T, class A> T& vector<T,A>::operator[](int n)

{

  return elem[n];

}

Рассмотрим следующий пример:

vector<int> v(100);

v[–200] = v[200]; // Ой!

int i;

cin>>i;

v[i] = 999;  // повреждение произвольной ячейки памяти

Этот код компилируется и выполняется, обращаясь к памяти, не принадлежащей нашему объекту класса

vector
. Это может создать большие неприятности! В реальной программе такой код неприемлем. Попробуем улучшить наш класс
vector
, чтобы решить эту проблему. Простейший способ — добавить в класс операцию проверки доступа с именем
at()
.

struct out_of_range { /* ... */ }; // класс, сообщающий об ошибках,

// связанных с выходом за пределы допустимого диапазона

template<class T, class A = allocator<T> > class vector {

  // ...

  T& at(int n);                     // доступ с проверкой

  const T& at(int n) const;         // доступ с проверкой

  T& operator[](int n);             // доступ без проверки

  const T& operator[](int n) const; // доступ без проверки

  // ...

};

template<class T, class A > T& vector<T,A>::at(int n)

{

  if (n<0 || sz<=n) throw out_of_range();

  return elem[n];

}

template<class T, class A > T& vector<T,A>::operator[](int n)

// как прежде

{

  return elem[n];

}

Итак, мы можем написать следующую функцию:

void print_some(vector<int>& v)

{

  int i = –1;

  cin >> i;

  while(i!= –1) try {

    cout << "v[" << i << "]==" << v.at(i) << "\n";

262
{"b":"847443","o":1}