Читаем Программирование полностью

  if (size()==capacity())

    reserve(size() = 0 ? 8 : 2*size()); // убедимся, что

                                        // есть место

  // сначала копируем последний элемент в неинициализированную ячейку:

  alloc.construct(elem+sz,*back());

  ++sz;

  iterator pp = begin()+index; // место для записи значения val

  for (iterator pos = end()–1; pos!=pp; ––pos)

    *pos = *(pos–1); // переносим элемент на одну позицию вправо

  *(begin()+index) = val; // "insert" val

  return pp;

}

Обратите внимание на следующие факты.

• Итератор не может ссылаться на ячейку, находящуюся за пределами последовательности, поэтому мы используем указатели, такие как elem+space. Это одна из причин, по которым распределители памяти реализованы на основе указателей, а не итераторов.

• Когда мы используем функцию reserve(), элементы могут быть перенесены в новую область памяти. Следовательно, мы должны запомнить индекс вставленного элемента, а не итератор, установленный на него. Когда элементы вектора перераспределяются в памяти, итераторы, установленные на них, становятся некорректными — их можно интерпретировать как ссылки на старые адреса.

• Наше использование распределителя памяти A является интуитивным, но не точным. Если вам придется реализовывать контейнер, то следует внимательно изучить стандарт.

• Тонкости, подобные этим, позволяют избежать непосредственной работы с памятью на нижнем уровне. Естественно, стандартный класс vector, как и остальные стандартные контейнеры, правильно реализует эти важные семантические тонкости. Это одна из причин, по которым мы настоятельно рекомендуем использовать стандартную библиотеку, а не “кустарные” решения.

По причинам, связанным с эффективностью, мы не должны применять функции insert() и erase() к среднему элементу вектора, состоящего из 100 тыс. элементов; для этого лучше использовать класс list (и класс map; см. раздел 21.6). Однако операции insert() и erase() можно применять ко всем векторам, а их производительность при перемещении небольшого количества данных является непревзойденной, поскольку современные компьютеры быстро выполняют такое копирование (см. упр. 20). Избегайте (связанных) списков, состоящих из небольшого количества маленьких элементов.

<p id="AutBody_Root386"><strong>20.9. Адаптация встроенных массивов к библиотеке STL</strong></p>

Мы многократно указывали на недостатки встроенных массивов: они неявно преобразуют указатели при малейшем поводе, их нельзя скопировать с помощью присваивания, они не знают своего размера (см. раздел 20.5.2) и т.д. Кроме того, мы отмечали их преимущества: они превосходно моделируют физическую память.

Для того чтобы использовать преимущества массивов и контейнеров, мы можем создать контейнер типа array, обладающий достоинствами массивов, но не имеющий их недостатков. Вариант класса array был включен в стандарт как часть технического отчета Комитета по стандартизации языка С++. Поскольку свойства, включенные в этот отчет, не обязательны для реализации во всех компиляторах, класс array может не содержаться в вашей стандартной библиотеке. Однако его идея проста и полезна.

  template // не вполне стандартный массив

struct array {

  typedef T value_type;

  typedef T* iterator;

  typedef T* const_iterator;

  typedef unsigned int size_type; // тип индекса

  T elems[N];

  // не требуется явное создание/копирование/уничтожение

  iterator begin() { return elems; }

  const_iterator begin() const { return elems; }

  iterator end() { return elems+N; }

  const_iterator end() const { return elems+N; }

  size_type size() const;

  T& operator[](int n) { return elems[n]; }

  const T& operator[](int n) const { return elems[n]; }

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

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

  T * data() { return elems; }

  const T * data() const { return elems; }

};

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже