На этом шаге мы рассмотрим более сложный пример.
Следующий пример предназначен для опытных программистов, хорошо разбирающихся в STL. Он дает представление как о мощи STL, так и о некоторых недостатках, присущих этой библиотеке. В частности, демонстрируются приемы:
//--------------------------------------------------------------------------- #include <vcl.h> #include <iostream> #include <iterator> #include <iomanip> #include <map> #include <string> #include <algorithm> #include <conio.h> //необходимо для getch() #pragma hdrstop //--------------------------------------------------------------------------- #pragma argsused using namespace std; std::string ToRus(const std::string &in) { char *buff = new char [in.length()+1]; CharToOem(in.c_str(),buff); std::string out(buff); delete [] buff; return out; } // Объект функции для сравнения строк // - позволяет задать критерий сравнения во время выполнения // - позволяет сравнивать символы без учета регистра class RuntimeStringCmp { public: // Константы режима сравнения enum cmp_mode {normal, nocase}; private: // Используемый режим сравнения const cmp_mode mode; // Вспомогательная функция для сравнения символов без учета регистра static bool nocase_compare (char c1, char c2) { return toupper(c1) < toupper(c2); } public: // Конструктор: инициализация критерия сравнения RuntimeStringCmp (cmp_mode m=normal) : mode(m) {} // Сравнение bool operator() (const string& s1, const string& s2) const { if (mode == normal) { return s1 < s2; } else { return lexicographical_compare (s1.begin(), s1.end(), s2.begin(), s2.end(), nocase_compare); } } }; // Тип контейнера: // - отображение // - ключ: string // - значение: string // - специальный тип критерия сравнения typedef map<string,string,RuntimeStringCmp> StringStringMap; // Функция для заполнения контейнера и вывода его содержимого void fillAndPrint(StringStringMap& coll); int main(int argc, char* argv[]) { // Создание контейнера с критерием сравнения по умолчанию cout << ToRus("Контейнер 1 (Исходные данные):\n"); StringStringMap coll1; fillAndPrint(coll1); // Создание объекта для сравнений без учета регистра символов RuntimeStringCmp ignorecase(RuntimeStringCmp::nocase); // Создание контейнера с критерием сравнения без учета регистра cout << ToRus("Контейнер 2 (Упорядоченные данные):\n"); StringStringMap coll2(ignorecase); fillAndPrint(coll2); getch(); return 0; } void fillAndPrint(StringStringMap& coll) { // Вставка элементов в произвольном порядке coll["Deutschland"] = "Германия"; coll["deutsch"] = "немецкий"; coll["Haken"] = "крючок"; coll["arbeiten"] = "работать"; coll["Hund"] = "собака"; coll["gehen"] = "идти"; coll["Unternehmen"] = "компания"; coll["unternehmen"] = "принять"; coll["gehen"] = "ходить"; coll["Bestatter"] = "услуги"; // Вывод элементов StringStringMap::iterator pos; cout.setf(ios::left,ios::adjustfield); for (pos=coll.begin(); pos!=coll.end(); ++pos) { cout << setw(15) << pos->first.c_str() << " " << ToRus(pos->second) << endl; } cout << endl; } //---------------------------------------------------------------------------
Функция main() создает два контейнера и вызывает для них функцию fillAndPrint(), которая заполняет контейнеры одинаковыми элементами и выводит их содержимое. Однако контейнеры используют разные критерии сортировки.
Результат выполнения программы выглядит так:
Рис.1. Результат выполения приложения
В первой части итоговых данных выводится содержимое первого контейнера, сравнивающего элементы оператором <. Сначала перечисляются все ключи, начинающиеся с символов верхнего регистра, а за ними следуют ключи, начинающиеся с символов нижнего регистра.
Во второй части ключи сравниваются без учета регистра, поэтому порядок перечисления элементов изменяется. Обратите внимание - вторая часть содержит на одну строку меньше первой. Дело в том, что при сравнении без учета регистра слова "Unternehmen" и "unternehmen" оказываются равными, а критерий сортировки нашего отображения не допускает наличия дубликатов. К сожалению, в результате возникает путаница - ключу, которому должно соответствовать значение "компания", соответствует значение "принять". Вероятно, в этом примере было бы правильнее использовать мультиотображение. Обычно в качестве словарей применяется именно этот тип контейнера.
Со следующего шага мы начнем рассматривать другие контейнеры STL.