Чтение онлайн

ЖАНРЫ

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

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). Избегайте (связанных) списков, состоящих из небольшого количества маленьких элементов.

20.9. Адаптация встроенных массивов к библиотеке STL

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

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

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

template <class T, int N> // не вполне стандартный массив

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; }

};

Это определение не полно и не полностью соответствует стандарту, но оно хорошо иллюстрирует основную идею. Кроме того, оно позволяет использовать класс

array
, если его нет в вашей стандартной библиотеке. Если же он есть, то искать его следует в заголовке
<array>
. Обратите внимание на то, что поскольку объекту класса
array<T,N>
известен его размер
N
, мы можем (и должны) предусмотреть операторы
=
,
==
,
!=
как для класса
vector
.

Например, используем массив со стандартной функцией

high
из раздела 20.4.2:

void f

{

array<double,6> a = { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5 };

array<double,6>::iterator p = high(a.begin, a.end);

cout << " максимальное значение " << *p << endl;

}

Обратите внимание на то, что мы не думали о классе

array
, когда писали функцию
high
. Возможность применять функцию
high
к объекту класса
array
является простым следствием того, что в обоих случаях мы придерживались стандартных соглашений.

20.10. Обзор контейнеров

В библиотеке STL есть несколько контейнеров.

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

Austern, Matt, ed. “Technical Report on C++ Standard Library Extensions,” ISO/IEC PDTR 19768. (Colloquially known as TR1.)

Austern, Matthew H. Generic Programming and the STL. Addison-Wesley, 1999. ISBN 0201309564. Koenig, Andrew, ed. The C++ Standard. Wiley, 2003. ISBN 0470846747. (Not suitable for novices.)

Lippman, Stanley B., Josee Lajoie, and Barbara E. Moo. The C++ Primer. AddisonWesley, 2005. ISBN 0201721481. (Use only the 4th edition.)

Musser, David R., Gillmer J. Derge, and Atul Saini. STL Tutorial and Reference Guide: C++ Programming with the Standard Template Library, Second Edition. AddisonWesley, 2001. ISBN 0201379236.

Stroustrup, Bjarne. The C++ Programming Language. Addison-Wesley, 2000. ISBN 0201700735.

Документацию о реализации библиотеки STL и библиотеки потоков ввода-вывода компании SGI (Silicon Graphics International) можно найти на веб-странице www.sgi.com/tech/stl>. Обратите внимание, что на этой веб-странице приводятся законченные программы.

Документацию о реализации библиотеки STL компании Dinkumware можно найти на веб-странице www.dinkumware.com/manuals/default.aspx. (Имейте в виду, что существует несколько версий этой библиотеки.)

Поделиться с друзьями: