Библиотека STL (Standart Template Library)Источник: cyberguru
STL - Standart Template Library. Стандартная библиотека шаблонов. Эта библиотека представляет большой набор данных структур и алгоритмов. Кстати она разработана, что очень приятно Александром Степановым и Менг Ли работающих в Hewlett-Packard Lab, им помогал Д. Л. Муссер из Ренсселэровского политехнического института. STL - это не просто расширение, недавно он был принят комитетом по стантартизации ANSI/ISO в качестве составляющей стандартной библиотеки C++. STL поддерживает как компилятор Borland, для которого его реализовала Rogue Wave Software так и Microsoft. У STL есть несколько версий. Мы с Вами посмотрим стандартную версию для VC++ Microsoft естественно. В чем же главная идея STL ?. Это уменьшение зависимости от стандартных библиотек С++. Главная беда стандартных библиотек это очень тесная их связь с данными, что делает их очень неудобными для работы с типами данных пользователя. STL позволяет работать с любыми типами данных и производить над ними операции. Первое главное отличие STL это то, что она отделяет структуры данных от алгоритмов, которые с ними работают. Вторая главная особенность в том, что она не объектно-ориентированная. Это может выглядеть как недостаток, но это наверно не так. Она работает немного на другом уровне. На самом деле объектно-ориентированное программирование это только миф созданный Вашим компилятором. Я совершенно точно гарантирую, что способен написать код, который будет получать доступ к защищеным данным класса откуда угодно. Правда для этого нужно делать вставку на ассемблере. Кроме того код у неё очень компактен. ПробуемСейчас мы с вами с помощью STL решим задачу безразмерного массива целых чисел. Это просто если делать с помощью STL. Создаем проект Win 32 Console с именем StlStep2 как Hello Word. И пишем код. // StlStep2.cpp : Defines the entry point for the console application. Ну как ? Много нового? Все новое. Вместе с VC++ поставляються и все необходимые файлы для работы с STL при этом есть некоторые особенности, например, Вы заметили, что при объявлении vector не использовалось расширение *.h. Его можно не использовать, но кроме того его и нет. Данный файл идет без расширения. Данный пример это просто проба. Для использования STL нам нужно получить некоторые знания, в том числе и теоритические. Вот дальше мы и будет изучать. Если вы программировали на чистом C и решали подобные задачи, то можете представить какой код нужно написать для подобной задачи. Явно больше. Пространство именПространство имен namespace это новый элемент языка и для работы с STL мы обязаны принять его во внимание. Этот элемент создан для программ созданых из многих файлов, в которых есть опасность конфликта имен. Объявляется пространство имен командой namespace: C++ Спецификация Для использования рабочей области применяется команда using namespace: C++ Спецификация Посмотрим ? Создавайте проект Win32 Console, как Hello Word с именем TestNameSpace. И код. Объявляем различные области. // TestNameSpace.cpp : Defines the entry point for the console application. А вот так они используются. // TestNameSpace.cpp : Defines the entry point for the console application. Запустите посмотрите результат. Все работает как часы. Шаблоны функций - основа STLМожно смело сказать, что основу STL составляют шаблоны. Именно они позволяют значительно сократить количество кода для программирования алгоритмов. Давайте расмотрим задачу, смысл которой в одинаковых математических расчетах для разных типов. Представьте, что Вам нужно вычислять функцию: (x*x)-(2*x) Для типа int и double. Что делается классически ??? Пишутся две функции. Например так: #include "stdafx.h" Если то же самое придется вычислять для других типов, то как Вы догадываетесь придется писать еще одну функцию. Выход из этой ситуации в применении шаблонов: #include "stdafx.h" Шаблон начинается словом template, после описания фигурные скобки: template < [список типов] [, [ список аргументов ]] > Обратите внимание на return. Это именно шаблон функции, а не шаблон класса. Шаблоны классовВ прошлый раз я заикнулся про шаблоны классов. Раз сказал надо показать. Шаблоны классов очень сильно похожи на шаблоны функций и решают теже задачи. То есть они помогают производить одинаковые операции с разными типами данных. Давайте прошлый пример переложим на классы: #include "stdafx.h" Ну, а теперь с шаблоном !!! #include "stdafx.h" Ну как ??? Впечатляет ??? Обратите внимание на Funct< int > cI(25); именно здесь задается тип класса. Это немного непревычно. Компоненты STLВ STL большое количество шаблонов, как классов так и функций. Мы можем их использовать с ООП или без него. Вообщем как хотим. Но в STL есть 3 основные компоненты.
Итератор - это аналог указателя, с помощью них мы можем получать доступ к различных элементам данных. Можно использовать и пару итераторов для задания диапазона. Как и указатель для получения данных из итераторов их необходимо разыменовать с помошью операции *. Всего есть пять классов итераторов.
Контейнеры - это структуры данных такие как списки, очереди и так далее. Доступ к данным находящимся внутри контейнера осуществляется с помощью итераторов :-) Есть следующие контейнеры. Могу пропустить, так что извините, если что.
Алгоритмы - это шаблоны функций, с помощью которых производятся операции по работе с данными. Например сортировки или поиска. Знакомимся с векторомВектор (vector) напоминает нам массив, только он способен расти до произвольного размера, поддерживает информацию о размере. Как и массив к вектору можно обратить воспользовавшись операцией индексирования []. Вот характеристики:
Как видите вектор оптимален для получения информации, но при большом количестве вставок лучше воспользоваться другими контейнерами, например, списками. Проблема в том, что физически вектор распологается в непрерывной памяти. На C это реализовывали функциями malloc. Для работы с вектором необходимо подключить заголовочный файл: #include "vector" Объявить рабочую область: using namespace std; После этого вектор необходимо объявить, это можно сделать двумя способами. vector< int > vArray1; В первом случае указывается пустой вектор, а во втором начальный размер. Можно получать информацию о параметрах вектора:
Смотрим пример: // TestVector.cpp : Defines the entry point for the console application. А вот результат: Size Vector 30 Как видите Size показывает сколько сейчас лежит в векторе чисел. В то время как capacity возвращает инициализированный размер, то есть тот размер, до которого можно добавлять данные без инициализации. Вас не удивило, что размер доступной памяти не изменился ??? Это размер доступного блока, а не всей памяти поэтому он и не изменился. Дальше о вектореЯ уже говоил о инициализации вектора. В дополнение можно сказать, что вектор можно инициализировать с заранее установленными значениями. Вот пример демонстрирующий и доступ к данным вектора через []. vector vVec(5,10); У вектора есть много полезных функций. Например, заполнить часть вектора необходимыми данными. В данном примере первые три элемента заполняются цифрой два: vVec.assign(3,2); Можно получить первый и последний элемент вектора, для этого есть функции front() и back(). vVec.assign(5,1); Вставку элемента с перемещением можно сделать функцией insert. Вставка производится в первую позицию с перемещением элементов вниз. for (x=0;x < 5;x++) Можно поместить число в конец вектора воспользовавшись функцией push_back(): vVec.push_back(99); Можно удалить последний элемент с сокращением размера: vVec.pop_back(); Для удаления используеться функция erase(): vVec.erase(vVec.begin()+2,vVec.begin()+4 ); Изменяет размер вектора функция resize(): vVec.resize(3); Применение алгоритмов к векторуОдним из алгоритмов является сортировка, вот мы и посмотрим как она работает с вектором. Для сортировки можно применить стандартный алгоритм sort. Для его использования необходимо подключить файл заголовков алгоритмов. #include "algorithm" После чего можно сортировать, как весь вектор, так и отдельные его части, что очень приятно. vector< int > v1(10); Я не буду подробно описывать, как работает данная сортировка это относится больше к алгоритмам, но здесь показано, что можно векторы сортировать и с ними работают стандартные алгоритмы. Наш класс в вектореНа данный момент мы использовали в векторе стандартные классы MFC, а как быть для того, чтобы в вектор можно было пеместить произвольный класс ? Для этого нужно соблюдать ряд условий. Минимальные условия.
Давайте реализуем и попробуем. // СlassVec.cpp : Defines the entry point for the console application. Естественно, это только самые базовые возможности. Для полного функционирования потребуется перегрузить достаточное количество операций. Довольно много. Как определить необходимость перегрузки данной операции ? Компилятор сам скажет :-)) в виде error :-). Мы знаем, что вектор может работать с архивом, а наш класс пока не умеет, и сортировка вряд ли будет нормальная пока не определены правила, как определить кто старше или больше !!! СпискиДля использования списков необходимо подключить заголовочный файл и выбрать область. #include "list" Теперь список можно объявлять. list< int > intList; Сначала я объявляю просто пустой список способный хранить числа, дальше я указываю, что в списке 3 элемента и в последнем случае я еще и нициализирую этот список числом 1. Количество элементов определяем функцией size(): cout << intListInit.size() << endl; Можно проверить список на пустоту. if (intList.empty()) cout << " int List empty " << endl; Для вставки можно использовать три метода Insert(), push_back(), push_front(). // testList.cpp : Defines the entry point for the console application. Из списка можно удалять элементы равные определенному значению. intListInit.remove(1); Я немного сумбурно расказываю о списках, и вот по какой причине. Очень многие возможности векторов списков и так далее реализуются через итераторы. А вот про них я и не рассказал. Например, чтобы удалить произвольный элемент списка нужен итератор. Наверно в следующем шаге я остановлюсь на понятии итератор и всё станет проще. Понимание итератораВы должны воспринимать итератор на данный момент, как указатель C++. В общем случае это одно и тоже. Давайте посмотрим как применить алгоритм find к массиву C. Массив это и есть набор указателей вроде как. // TestIter.cpp : Defines the entry point for the console application. Вот смотрите, мы с вами передали в функцию find два указателя. Указатель начала массива и указатель конца массива. Это и есть как их называют - итераторы. При нахождении вам вернется итератор значения, чтобы проверить, что ничего нет мы проверяем с итератором за концом массива. Вы уже видели, как используются подобные функции типа begin() и end() для получения начала и конца итераторов. Главный вопрос почему не пользоваться указателями ??? Не получится. Представим массив из структур. struct A Так вот для того, чтобы работать с указателями вы должны делать примерно так. A* D; Только так вы можете идти от одной структуры к другой. И теперь самое главное. ЭТО МОЖНО ТОЛЬКО ЕСЛИ СТРУКТУРЫ РАСПОЛОЖЕНЫ ЛИНЕЙНО, то есть одна за другой. Тут же мы получим ограничение на то, что блок памяти должен быть непрерывный. Это очень не удобно и ограничивает размер при наличии памяти. Выход только один ЗАВЕСТИ ОТДЕЛЬНЫЙ МАССИВ С ИТЕРАТОРАМИ(указателями). В нем будут линейно храниться указатели на элементы. Ну и что скажете Вы ? Все равно нужен линейный блок памяти. Нужет только структуры могут быть по размеру очень большими. Даже в моем примере на место данной структуры можно поместить массу указателей. И теперь размер ограничивается непрерывной памятью для указателей. Это намного больше, кроме того сами структуры могут быть рассортированны по всей памяти. Итак, давайте попробуем сформулировать, что такое итератор на данной степени понимания. Это массив указателей на элементы структур. Некоторые преимущества класса setПрежде всего - здравствуйте. Ну, если вы все-таки читаете этот шаг, значит либо вы - Каев Артем, либо Артем счел мой скромный мысленный напряг достойным того, чтобы повесить его в своем разделе, что, конечно же, весьма и весьма приятно. Итак, класс set. Данный шаг, конечно же, не ставит своей целью охватить все возможности класса. Просто мне хотелось немного поговорить о тех его свойствах, которые кажутся мне интересными и довольно-таки часто оказывались мне полезны. Но сначала. Возможно, я немного повторю Артема если скажу: set или, говоря языком математики, множество, представляет собой объект, контролирующий произвольной длины последовательность уникальных элементов какого-либо типа. В общем, классический подход ко множеству такой: set используется для хранения неповторяющихся (уникальных) ключей, либо для проверки, есть ли элемент в наборе данных. Ну, от подобных традиций иногда следует отступать, то есть, конечно же, тогда, когда это может оказаться полезным. Находить новые пути и все такое прочее. Сразу скажу: все элементы, помещенные во множество, оказываются рассортированы в порядке возрастания. Это может оказаться полезным, или же наоборот, но в любом случае необходимо это учитывать. Я впервые столкнулся со множеством при написании программы, демонстрирующей свойства интерполяционных поленомов. Пользователь должен был вводить точки, а я (то есть програма) - строить графики. Но вот в чем возникла загвоздочка: в любой функции по опреденению не может быть повторяющихся абсцисс (то есть иксов с разными игреками). Что делать? Писать проверочку ручками (да и сортировать вдобавок!) - брала тоска. Выход я нашел такой (привожу пример, конечно же, сильно упрощенный): 1. Создайте проект Win32 Console Application (пустой). Назовем, например, set_test. 2. В нем создайте два файла: заголовок (set_test.h) и cpp-шник (соответственно, set_test.cpp). Хотя можете, конечно, хранить все в одном: проект маленький. 3. В заголовке (set_test.h) напишите: #include <set> // это подключаем класс множеств Что сие значит? Во-первых, почему именно оператор "меньше". Эта прелесть (set) осуществляет все свои сравнения (по крайней мере, при попытке добавить элемент) именно через оператор "меньше". Как узнать, какой оператор перегружать? А вы попробуйте откомпилить проект (когда мы его закончим) без его перегрузки. Выскочит соответствующий error... :) . Во-вторых, почему именно такая перегрузка. Ну, мне же нужно повторяющиеся иксы откинуть. И вдобавок по их значениям рассортировать. Вот я и заставляю беднягу-компа сравнивать вместо двух классов два икса... поделом... :) Но закончим с проектом. Пишем cpp-шник: #include "set_test.h" // подключаем, что уже выстрадали Итак, что означает фраза if(!set_exact.insert(stl_point(x, y)).second) Во-первых, вызвывается конструктор stl_point(x, y) - в созданный экземпляр класса сразу же кидаются икс с игреком (см. конструктор). Далее. Возвращаемое значение сразу же кидается в set_exact и не запоминается больше нигде: set_exact.insert(stl_point(x, y)) Метод insert объекта set возвращает простенький объект типа pair: template Как видите, pair чем-то подобен моей точке: все, что содержит - это два элемента любого (возможно, одинакового) типа - под загадочными именами first и second. :) Наш же insert в случае удачи возвращает pair(it, true), иначе - pair(it, false). Ну, что такое загадочное it - никогда не интересовался. Наверное, на воткнутый элемент итератор. А вот второй элемент, second, - правда ведь на определенную мысль наталкивает? Итак, смотрим что там, у second'а внутри. Если элемент воткнулся - радуемся. Нет - гм... Ну я печалиться не буду :)) Компилим проект. Запускаем. Попробуйте ввести элементы с одинаковыми иксами. И наоборот - с одинаковыми игреками. Да и вообще - потестьте :)) Теперь усложняем проект. Смотрите, чем поменялся main: #include "set_test.h" // подключаем, что уже выстрадали |