Программирование >>  Разработка устойчивых систем 

1 ... 101 102 103 [ 104 ] 105 106 107 ... 196


Функция insertO пытается вставить элемент в контейнер; если элемент с таким значением уже присутствует, попытка игнорируется. Чаще всего операции с множествами ограничиваются вставкой и проверкой наличия элемента в множестве. Кроме того, можно вычислить объединение, пересечение или разность двух множеств, а также проверить, является ли одно множество подмножеством другого. В нашем примере значения 0-9 вставляются в множество 25 раз, но принимаются только 10 уникальных значений.

Теперь попробуем приспособить программу Intset.cpp для вывода списка слов, использованных в документе. Решение оказывается на удивление простым:

: C07:WordSet.cpp linclude <fstream> linclude <iostream> linclude <iterator> linclude <set> linclude <string> linclude . ./require.h using namespace std;

void wordSetCchar* fileName) { ifstream source(fileName); assure(source. fileName); string word; set<string> words; while(source word)

words.insert(word); copy(words.begin(). words.endO,

ostream iterator<string>(cout. \n )); cout Number of unique words;

words.SizeO endl;

int main(int argc. char* argv[]) { if(argc > 1)

wordSet(argv[l]); else

wordSet( WordSet.cpp ); } III:-

Единственное принципиальное различие состоит в том, что в новой версии программы множество содержит строки вместо целых чисел. Слова читаются из файла, но остальные операции сходны с теми, которые использовались в программе Intset.cpp. В выходных данных отсутствуют дубликаты, а вследствие особенностей реализации множеств слова автоматически сортируются.

Множество относится к числу ассоциативных контейнеров - одной из трех категорий контейнеров стандартной библиотеки С++. Эти категории и входящие в них контейнеры перечислены ниже:

последовательные контейнеры - vector, list, deque;

контейнерные адаптеры - queue, stack, priority queue;

ассоциативные контейнеры - set, map, multiset, multimap.

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



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

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

Вектор, как вы уже знаете, представляет собой линейную последовательность с произвольным доступом к элементам. Тем не менее, вставка элемента в середину контейнера, хранящегося в памяти в виде непрерывного блока, обходится достаточно дорого; это относится и к векторам, и к массивам. Дек (deque, сокращение от double-ended queue , то есть двусторонняя очередь ) также обеспечивает произвольный доступ, почти не уступающий векторам по скорости, но он гораздо быстрее работает при выделении новой памяти. Кроме того, дек обеспечивает быструю вставку новых элементов в начало и конец последовательности. Список (list) является двусвязным, поэтому перебор элементов в нем происходит медленно, но зато быстро выполняется вставка элемента в произвольной позиции. Таким образом, список, дек и вектор обладают сходной базовой функциональностью (все эти контейнеры содержат линейные последовательности элементов), но различаются по сложности выполнения операций. Если вы только осваиваете программирование с применением контейнеров, выберите один тип контейнера и пользуйтесь им, экспериментируя с остальными типами только на стадии оптимизации программы.

Для большинства задач, которые вам предстоит решать, простых последовательных контейнеров (векторов, деков и списков) будет вполне достаточно. Все три разновидности содержат функцию push back(), используемую для вставки новых элементов в конец последовательности (у деков и списков также имеется функция push front(), предназначенная для вставки элементов в начало последовательности).

Но как производится выборка элементов в последовательном контейнере? При работе с деками и векторами можно воспользоваться оператором индексирования [ ], но со списками этот способ не работает. Для обращения к элементам всех трех разновидностей последовательных контейнеров можно применять итераторы. Каждый контейнер предоставляет соответствующий тип итератора для обращения к своим элементам.

Хотя объекты хранятся в контейнерах по значению (то есть в контейнере сохраняется копия всего объекта), в некоторых ситуациях бывает удобнее хранить указатели на объекты. Такой подход позволяет заменять объекты в пределах иерархии и учитывать полиморфное поведение классов. Рассмотрим классический пример с геометрическими фигурами, для которых определен общий набор операций, а также операции, специфические для каждого конкретного класса. В следующей программе вектор STL содержит указатели на разные виды объектов Shape, созданных в куче:

: C07:Stlshape.срр

Работа с классами геометрических фигур в STL #1 nclude <vector> linclude <iostream> using namespace std:



class Shape { public:

virtual void drawO = 0:

virtual -ShapeO {}:

class Circle : public Shape { public:

void drawO { cout Circle: :draw\n : } -CircleO { cout ~Circle\n : }

class Triangle : public Shape { public:

void drawO { cout Triangle: :draw\n ; } -TriangleO { cout -TriangleVn : }

class Square : public Shape { public:

void drawO { cout Square::draw\n : } -SquareO { cout -Square\n : }

typedef std::vector<Shape*> Container: typedef Container::iterator Iter:

int mainO { Container shapes: shapes.push back(new Circle): shapes.push back(new Square): shapes.push back(new Triangle): fordter i = shapes.beginO: i != shapes.endO: i++) (*i)->draw(): ... Завершение работы с фигурами: fordter j = shapes.beginO: j != shapes.end(): j++) delete *j: } III:-

Определения классов Shape, Circle, Square и Triangle должны выглядеть вполне знакомо. Абстрактный базовый класс Shape (о чем говорит спецификатор =0) определяет интерфейс для всех разновидностей фигур. Производные классы переопределяют виртуальную функцию draw() для выполнения операции, соответствующей данному типу. Теперь мы хотим создать несколько разнотипных объектов Shape и сохранить их в контейнере STL. Рассмотрим вспомогательное определение типа

typedef std::vector<Shape*> Container:

Это определение создает синоним для вектора с элементами Shape*, а следующее определение использует его и создает другой синоним для vector<Shape*>::iterator:

typedef Container::iterator Iter:

Обратите внимание: чтобы получить правильный тип итератора, определенный в виде вложенного класса, необходимо указать имя типа контейнера. Хотя в STL существуют разные виды итераторов (прямые, двусторонние, итераторы произ-



1 ... 101 102 103 [ 104 ] 105 106 107 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика