Шилдт c++_базовый_курс издание 3. Герберт Шилдт С базовый курс
Скачать 9.37 Mb.
|
Отображения Отображение — это ассоциативный контейнер. Класс map поддерживает ассоциативный контейнер, в котором уникальным ключам соответствуют определенные значения. По сути, ключ — это просто имя, которое присвоено некоторому значению. После того как значение сохранено в контейнере, к нему можно получить доступ, используя его ключ. Таким образом, в самом широком смысле отображение — это список пар "ключ-значение". Если нам известен ключ, мы можем легко найти значение. Например, мы могли бы определить отображение, в котором в качестве ключа используется имя человека, а в качестве значения — его телефонный номер. Ассоциативные контейнеры становятся все более популярными в программировании. Как упоминалось выше, отображение может хранить только уникальные ключи. Ключи- дубликаты не разрешены. Чтобы создать отображение, которое бы позволяло хранить неуникапьные ключи, используйте класс multimap. Контейнер map имеет следующую шаблонную спецификацию. template Allocator =allocator > >class map Здесь Key— тип данных ключей, T— тип сохраняемых (отображаемых) значений, а Comp — функция, которая сравнивает два ключа. По умолчанию в качестве функции сравнения используется стандартная функция-объект less. Элемент Allocator означает распределитель памяти, который по умолчанию использует стандартный распределитель allocator. Класс map имеет следующие конструкторы. explicit map(const Comp &cmpfn = Comp(), const Allocator &a = Allocator()); map(const map template &cmpfn = Comp(), const Allocator &a = Allocator()); Первая форма конструктора создает пустое отображение. Вторая предназначена для создания отображения, которое содержит те же элементы, что и отображение ob. Третья создает отображение, которое содержит элементы в диапазоне, заданном итераторами start и end. Функция, заданная параметром cmpfn (если она задана), определяет характер упорядочения отображения. В общем случае любой объект, используемый в качестве ключа, должен определять конструктор по умолчанию и перегружать оператор "<" (а также другие необходимые операторы сравнения). Эти требования для разных компиляторов различны. Для класса map определены следующие операторы сравнения: ==, <, <=, !=, > и >= Функции-члены, определенные для класса map, представлены в табл. 21.4. В их описании под элементом key_type понимается тип ключа, а под элементом value_type — значение выражения pair Пары "ключ-значение" хранятся в отображении как объекты класса pair, который имеет следующую шаблонную спецификацию. template struct pair { typedef Ktype first_type; // тип ключа typedef Vtype second_type; // тип значения Ktype first; // содержит ключ Vtype second; // содержит значение // Конструкторы pair(); pair (const Ktype &k, const Vtype &v); template } Как отмечено в комментариях, член first содержит ключ, а член second — значение, соответствующее этому ключу. Создать пару "ключ-значение" можно либо с помощью конструкторов класса pair, либо путем вызова функции make_pair(), которая создает парный объект на основе типов данных, используемых в качестве параметров. Функция make_pair() — это обобщенная функция, прототип которой имеет следующий вид. template pair Как видите, функция make_pair() возвращает парный объект, состоящий из значений, типы которых заданы параметрами Ktype и Vtype. Преимущество использования функции make_pair() состоит в том, что типы объектов, объединяемых в пару, определяются автоматически компилятором, а не явно задаются программистом. Возможности использования отображения демонстрируется в следующей программе. В данном случае в отображении сохраняется 10 пар "ключ-значение". Ключом служит символ, а значением — целое число. Пары "ключ-значение" хранятся следующим образом: А 0 В 1 С 2 и т.д. После сохранения пар в отображении пользователю предлагается ввести ключ (т.е. букву из диапазона А-J), после чего выводится значение, связанное с этим ключом. // Демонстрация использования простого отображения. #include #include char ch; cout << "Введите ключ: "; cin >> ch; map // Находим значение по заданному ключу. р = m.find(ch); if(р != m.end()) cout << p->second; else cout << "Такого ключа в отображении нет.\n"; return 0; } Обратите внимание на использование шаблонного класса pair для построения пар "ключ-значение". Типы данных, задаваемые pair-выражением, должны соответствовать типам отображения, в которое вставляются эти пары. После инициализации отображения ключами и значениями можно выполнять поиск значения по заданному ключу, используя функцию find(). Эта функция возвращает итератор, который указывает на нужный элемент или на конец отображения, если заданный ключ не был найден. При обнаружении совпадения значение, связанное с ключом, можно найти в члене second парного объекта типа pair. В предыдущем примере пары "ключ-значение" создавались явным образом с помощью шаблона pair m.insert(make_pair((char) ('А'+i), i)); Здесь, как видите, выполняется операция приведения к типу char, которая необходима для переопределения автоматического преобразования в тип int результата сложения значения i с символом 'А'. Хранение в отображении объектов класса Подобно всем другим контейнерам, отображение можно использовать для хранения объектов создаваемых вами типов. Например, следующая программа создает простой словарь на основе отображения слов с их значениями. Но сначала она создает два класса word и meaning. Поскольку отображение поддерживает отсортированный список ключей, программа также определяет для объектов типа word оператор "<". В общем случае оператор "<" следует определять для любых классов, которые вы собираетесь использовать в качестве ключей. (Некоторые компиляторы могут потребовать определения и других операторов сравнения.) // Использование отображения для создания словаря. #include #include class meaning { char str[80]; public: meaning() { strcmp(str, " ");} meaning(char *s) { strcpy(str, s); } char *get() { return str; } }; int main() { map /* Помещаем в отображение объекты классов word и meaning. */ dictionary.insert( pair (word("дом"), meaning("Место проживания."))); dictionary.insert( pair meaning("Устройство ввода данных."))); dictionary.insert( pair (word("программирование"), meaning("Процесс создания программы."))); dictionary.insert( pair (word("STL"), meaning("Standard Template Library"))); // По заданному слову находим его значение. char str[80]; cout << "Введите слово: "; cin >> str; map р = dictionary.find(word(str)); if(p != dictionary.end()) cout << "Определение: " << p->second.get(); else cout << "Такого слова в словаре нет.\n"; return 0; } Вот один из возможных вариантов выполнения этой программы. Введите слово: дом Определение: Место проживания. В этой программе каждый элемент отображения представляет собой символьный массив, который содержит строку с завершающим нулем. Ниже в этой главе мы рассмотрим более простой вариант построения этой программы, в которой использован стандартный тип string. Алгоритмы Алгоритмы обрабатывают данные, содержащиеся в контейнерах. Несмотря на то что каждый контейнер обеспечивает поддержку собственных базовых операций, стандартные алгоритмы позволяют выполнять более расширенные или более сложные действия. Они также позволяют работать с двумя различными типами контейнеров одновременно. Для получения доступа к алгоритмам библиотеки STL необходимо включить в программу заголовок В библиотеке STL определено множество алгоритмов, которые описаны в табл. 21.5. Все эти алгоритмы представляют собой шаблонные функции. Это означает, что их можно применять к контейнеру любого типа. Подсчет элементов Одна из самых популярных операций, которую можно выполнить для любой последовательности элементов, — подсчитать их количество. Для этого можно использовать один из алгоритмов: count() или count_if(). Общий формат этих алгоритмов имеет следующий вид. template ptrdiff_t count(InIter start, InIter end, const T &val); template ptrdiff_t count_if(InIter start, InIter end, UnPred pfn); Алгоритм count() возвращает количество элементов, равных значению val, в последовательности, границы которой заданы параметрами start и end. Алгоритм count_if(), действуя в последовательности, границы которой заданы параметрами start и end, возвращает количество элементов, для которых унарный предикат pfn возвращает значение true. Тип ptrdiff_t определяется как некоторая разновидность целочисленного типа. Использование алгоритмов count() и count_if() демонстрируется в следующей программе. /* Демонстрация использования алгоритмов count и count_if. */ #include #include #include #include using namespace std; /* Это унарный предикат, который определяет, представляет ли данный символ гласный звук. */ bool isvowel(char ch) { ch = tolower(ch); if(ch=='a' || ch=='e' || ch=='и' || ch=='o' || ch=='у' || ch=='ы' || ch=='я' || ch=='ё' || ch=='ю' || ch=='э') return true; return false; } int main() { char str[] = "STL-программирование — это сила!"; vector unsigned int i; for(i=0; str[i]; i++) v.push_back(str[i]); cout << "Последовательность: "; for(i=0; i int n; n = count (v.begin(), v.end(), 'м'); cout << n << " символа м\n"; n = count_if(v.begin(), v.end(), isvowel); cout << n << " символов представляют гласные звуки.\n"; return 0; } При выполнении эта программа генерирует такие результаты. Последовательность: STL-программирование -- это сила! 2 символа м 11 символов представляют гласные звуки. Программа начинается с создания вектора, который содержит строку "STL- программирование - это сила!". Затем используется алгоритм count() для подсчета количества букв 'м' в этом векторе. После этого вызывается алгоритм count_if(), который подсчитывает количество символов, представляющих гласные звуки с использованием в качестве предиката функции isvowel(). Обратите внимание на то, как закодирован этот предикат. Все унарные предикаты получают в качестве параметра объект, тип которого совпадает с типом элементов, хранимых в контейнере, для которого и создается этот предикат. Предикат должен возвращать значение ИСТИНА или ЛОЖЬ. Удаление и замена элементов Иногда полезно сгенерировать новую последовательность, которая будет состоять только из определенных элементов исходной последовательности. Одним из алгоритмов, который может справиться с этой задачей, является remove_copy(). Его общий формат выглядит так. template OutIter remove_copy(InIter start, InIter end, OutIter result, const T &val); Алгоритм remove_copy() копирует с извлечением из заданного диапазона элементы, которые равны значению val, и помещает результат в последовательность, адресуемую параметром result. Алгоритм возвращает итератор, указывающий на конец результата. Контейнер-приемник должен быть достаточно большим, чтобы принять результат. Чтобы в процессе копирования один элемент в последовательности заменить другим, используйте алгоритм replace_copy(). Его общий формат выглядит так. template OutIter replace_copy(InIter start, InIter end, OutIter result, const T &old, Const T &new); Алгоритм replace_copy() копирует элементы из заданного диапазона в последовательность, адресуемую параметром result. В процессе копирования происходит замена элементов, которые имеют значение old, элементами, имеющими значение new. Алгоритм помещает результат в последовательность, адресуемую параметром result, и возвращает итератор, указывающий на конец этой последовательности. Контейнер- приемник должен быть достаточно большим, чтобы принять результат. В следующей программе демонстрируется использование алгоритмов remove_copy() и replace_copy(). При ее выполнении создается последовательность символов, из которой удаляются все буквы 'т'. Затем выполняется замена всех букв 'о' буквами 'Х'. /* Демонстрация использования алгоритмов remove_copy и replace_copy. */ #include #include #include using namespace std; int main() { char str[] = "Это очень простой тест."; vector unsigned int i; for(i=0; str[i]; i++) v.push_back(str[i]); // **** демонстрация алгоритма remove_copy **** cout << "Входная последовательность: "; for(i=0; i cout << endl; // Удаляем все буквы 'т'. remove_copy(v.begin(), v.end(), v2.begin(), 'Т'); cout << "После удаления букв 'т' : "; for(i=0; v2[i]; i++) cout << v2[i]; cout << endl << endl; // **** Демонстрация алгоритма replace_copy **** cout << "Входная последовательность: "; for(i=0; i cout << endl; // Заменяем буквы 'о' буквами 'Х'. replace_copy(v.begin(), v.end(), v2.begin(), 'о', 'Х'); cout << "После замены букв 'o' буквами 'Х': "; for(i=0; v2[i]; i++) cout << v2[i]; cout << endl << endl; return 0; } Результаты выполнения этой программы таковы. Входная последовательность: Это очень простой тест. После удаления букв 'т': Эо очень просой ес. Входная последовательность: Это очень простой тест. После замены букв 'о' буквами 'Х': ЭтХ Хчень прХстХй тест. Реверсирование последовательности В программах часто используется алгоритм reverse(), который в диапазоне, заданном параметрами start и end, меняет порядок следования элементов на противоположный. Его общий формат имеет следующий вид. template void reverse(BiIter start, BiIter end); В следующей программе демонстрируется использование этого алгоритма. // Демонстрация использования алгоритма reverse. #include #include #include using namespace std; int main() { vector unsigned int i; for(i=0; i<10; i++) v.push_back(i); cout << "Исходная последовательность: "; for(i=0; i cout << endl; reverse (v.begin(), v.end()); cout << "Реверсированная последовательность: "; for(i=0; i } Результаты выполнения этой программы таковы. Исходная последовательность: 0123456789 Реверсированная последовательность: 9876543210 Преобразование последовательности Одним из самых интересных алгоритмов является transform(), поскольку он позволяет модифицировать каждый элемент в диапазоне в соответствии с заданной функцией. Алгоритм transform() используется в двух общих форматах. template OutIter transform(InIter start, InIter end, OutIter result, Func unaryfunc); template OutIter transform(InIter1 start1, InIter1 end1, InIter2 start2, OutIter result, Func binaryfunc); Алгоритм transform() применяет функцию к диапазону элементов и сохраняет результат в последовательности, заданной параметром result. В первой форме диапазон задается параметрами start и end. Применяемая для преобразования функция задается параметром unaryfunc. Она принимает значение элемента в качестве параметра и должна возвратить преобразованное значение. Во второй форме алгоритма преобразование выполняется с использованием бинарной функции, которая принимает в качестве первого параметра значение предназначенного для преобразования элемента из последовательности, а в качестве второго параметра — элемент из второй последовательности. Обе версии возвращают итератор, указывающий на конец результирующей последовательности. В следующей программе используется простая функция преобразования xform(), которая возводит в квадрат каждый элемент списка. Обратите внимание на то, что результирующая последовательность сохраняется в том же списке, который содержал исходную последовательность. // Пример использования алгоритма transform. #include #include #include using namespace std; // Простая функция преобразования. int xform(int i) { return i * i; // квадрат исходного значения } int main() { list int i; // Помещаем значения в список. for(i=0; i<10; i++) x1.push_back(i); cout << "Исходный список x1: "; list while(p != x1.end()) { cout << *p << " "; p++; } cout << endl; // Преобразование списка x1. p = transform(x1.begin(), x1.end(), x1.begin(), xform); cout << "Преобразованный список x1: "; p = x1.begin(); while(p != x1.end()) { cout << *p << " "; p++; } return 0; } При выполнении эта программа генерирует такие результаты. Исходный список x1: 0 1 2 3 4 5 6 7 8 9 Преобразованный список x1: 0 1 4 9 16 25 36 49 64 81 Как видите, каждый элемент в списке x1 теперь возведен в квадрат. Исследование алгоритмов Описанные выше алгоритмы представляют собой только малую часть всего содержимого библиотеки STL. И конечно же, вам стоит самим исследовать другие алгоритмы. Заслуживают внимания многие, например set_union() и set_difference(). Они предназначены для обработки содержимого такого контейнера, как множество. Интересны также алгоритмы next_permutation() и prev_permutation(). Они создают следующую и предыдущую перестановки элементов заданной последовательности. Время, затраченное на изучение алгоритмов библиотеки STL, — это время, потраченное не зря! Класс string Класс string обеспечивает альтернативу для строк с завершающим нулем. Как вы знаете, C++ не поддерживает встроенный строковый тип. Однако он предоставляет два способа обработки строк. Во-первых, для представления строк можно использовать традиционный символьный массив с завершающим нулем. Строки, создаваемые таким способом (он вам уже знаком), иногда называют С-строками. Во- вторых, можно использовать объекты класса string, и именно этот способ рассматривается в данном разделе. В действительности класс string представляет собой специализацию более общего шаблонного класса basic_string. Существует две специализации типа basic_string: тип string, который поддерживает 8-битовые символьные строки, и тип wstring, который поддерживает строки, образованные двухбайтовыми символами. Чаще всего в обычном программировании используются строковые объекты типа string. Для использования строковых классов C++ необходимо включить в программу заголовок Прежде чем рассматривать класс string, важно понять, почему он является частью С++- библиотеки. Стандартные классы не сразу были добавлены в определение языка C++. На самом деле каждому нововведению предшествовали серьезные дискуссии и жаркие споры. При том, что C++ уже содержит поддержку строк в виде массивов с завершающим нулем, включение класса string в C++, на первый взгляд, может показаться исключением из этого правила. Но это далеко не так. И вот почему: строки с завершающим нулем нельзя обрабатывать стандартными С++-операторами, и их нельзя использовать в обычных С++- выражениях. Рассмотрим, например, следующий фрагмент кода. char s1 [80], s2[80], s3[80]; s1 = "один"; // так делать нельзя s2 = "два"; // так делать нельзя s3 = s1 + s2; // ошибка Как отмечено в комментариях, в C++ невозможно использовать оператор присваивания для придания символьному массиву нового значения (за исключением инструкции инициализации), а также нельзя применять оператор "+" для конкатенации двух строк. Эти операции можно выполнить с помощью библиотечных функций. strcpy(s1, "one"); strcpy(s2, "two"); strcpy(s3, s1); strcat(s3, s2); Поскольку символьный массив с завершающим нулем формально не является самостоятельным типом данных, к нему нельзя применить С++-операторы. Это лишает "изящества" даже самые элементарные операции со строками. И именно неспособность обрабатывать строки с завершающим нулем с помощью стандартных С++-операторов привела к разработке стандартного строкового класса. Вспомните: создавая класс в C++, мы определяем новый тип данных, который можно полностью интегрировать в С++-среду. Это, конечно же, означает, что для нового класса можно перегружать операторы. Следовательно, вводя в язык стандартный строковый класс, мы создаем возможность для обработки строк так же, как и данных любого другого типа, а именно посредством операторов. Однако существует еще одна причина, оправдывающая создание стандартного класса string: безопасность. Руками неопытного или неосторожного программиста очень легко обеспечить выход за границы массива, который содержит строку с завершающим нулем. Рассмотрим, например, стандартную функцию копирования strcpy(). Эта функция не предусматривает механизм проверки факта нарушения границ массива-приемника. Если исходный массив содержит больше символов, чем может принять массив-приемник, то в результате этой ошибки очень вероятен полный отказ системы. Как будет показано ниже, стандартный класс string не допускает возникновения подобных ошибок. Итак, существует три причины для включения в C++ стандартного класса string: непротиворечивость данных (строка теперь определяется самостоятельным типом данных), удобство (программист может использовать стандартные С++-операторы) и безопасность (границы массивов отныне не будут нарушаться). Следует иметь в виду, что все выше перечисленное не означает, что вы должны отказываться от использования обычных строк с завершающим нулем. Они по-прежнему остаются самым эффективным средством реализации строк. Но если скорость не является для вас определяющим фактором, использование нового класса string даст вам доступ к безопасному и полностью интегрированному способу обработки строк. И хотя класс string традиционно не воспринимается как часть библиотеки STL, он, тем не менее, представляет собой еще один контейнерный класс, определенный в C++. Это означает, что он поддерживает алгоритмы, описанные в предыдущем разделе. При этом строки имеют дополнительные возможности. Для получения доступа к классу string необходимо включить в программу заголовок Класс string очень большой, он содержит множество конструкторов и функций-членов. Кроме того, многие функции-члены имеют несколько перегруженных форм. Поскольку в одной главе невозможно рассмотреть все содержимое класса string, мы обратим ваше внимание только на самые популярные его средства. Получив общее представление о работе класса string, вы сможете легко разобраться в остальных его возможностях самостоятельно. Прототипы трех самых распространенных конструкторов класса string имеют следующий вид. string(); string(const char *str); string (const string &str); Первая форма конструктора создает пустой объект класса string. Вторая форма создает string-объект из строки с завершающим нулем, адресуемой параметром str. Эта форма конструктора обеспечивает преобразование из строки с завершающим нулем в объект типа string. Третья создает string-объект из другого string-объекта. Для объектов класса string определены следующие операторы. Эти операторы позволяют использовать объекты типа string в обычных выражениях и избавляют программиста от необходимости вызывать такие функции, как strcpy() или strcat(). В общем случае в выражениях можно смешивать string-объекты и строки с завершающим нулем. Например, string-объект можно присвоить строке с завершающим нулем. Оператор "+" можно использовать для конкатенации одного string-объекта с другим или string-объекта со строкой, созданной в С-стиле (С-строкой). Другими словами, поддерживаются следующие операции. string-объект + string-объект string-объект + С-строка С-строка + string-объект Оператор "+" позволяет также добавлять символ в конец строки. В классе string определена константа npos, которая равна -1. Она представляет размер строки максимально возможной длины. Строковый класс C++ существенно облегчает обработку строк. Например, используя string-объекты, можно применять оператор присваивания для назначения string-объекту строки в кавычках, оператор "+" — для конкатенации строк и операторы сравнения — для сравнения строк. Выполнение этих операций демонстрируется в следующей программе. // Программа демонстрации обработки строк. #include #include using namespace std; int main() { string str1("Класс string позволяет эффективно "); string str2("обрабатывать строки."); string str3; // Присваивание string-объекта. str3 = str1; cout << str1 << "\n" << str3 << "\n"; // Конкатенация двух string-объектов. str3 = str1 + str2; cout << str3 << "\n"; // Сравнение string-объектов. if(str3 > str1) cout << "str3 > str1\n"; if(str3 == str1 + str2) cout << "str3 == str1+str2\n"; /* Объекту класса string можно также присвоить обычную строку. */ str1 = "Это строка с завершающим нулем.\n"; cout << str1; /* Создание string-объекта с помощью другого string-объекта. */ string str4 (str1); cout << str4; // Ввод строки. cout << "Введите строку: "; cin >> str4; cout << str4; return 0; } При выполнении эта программа генерирует такие результаты. Класс string позволяет эффективно Класс string позволяет эффективно Класс string позволяет эффективно обрабатывать строки. str3 > str1 str3 == str1+str2 Это строка с завершающим нулем. Это строка с завершающим нулем. Введите строку: Привет Привет Обратите внимание на то, как легко теперь выполняется обработка строк. Например, оператор "+" используется для конкатенации строк, а оператор ">" — для сравнения двух строк. Для выполнения этих операций с использованием С-стиля обработки строк, т.е. использования строк с завершающим нулем, пришлось бы применять менее удобные средства, а именно вызывать функции strcat() и strcmp(). Поскольку С++-объекты типа string можно свободно смешивать с С-строками, их (string-объекты) можно использовать в любой программе не только безо всякого ущерба для эффективности, но даже с заметным выигрышем. Важно также отметить, что в предыдущей программе размер строк не задается. Объекты типа string автоматически получают размер, нужный для хранения заданной строки. Таким образом, при выполнении операций присваивания или конкатенации строк строка- приемник увеличится по длине настолько, насколько это необходимо для хранения нового содержимого строки. При обработке string-объектов невозможно выйти за границы строки. Именно этот динамический аспект string-объектов выгодно отличает их от строк с завершающим нулем (которые часто страдают от нарушения границ). Обзор функций-членов класса string Если самые простые операции со строками можно реализовать с помощью операторов, то при выполнении более сложных не обойтись без функций-членов класса string. Класс string содержит слишком много функций-членов, мы же рассмотрим здесь только самые употребительные из них. Важно! Поскольку класс string— контейнер, он поддерживает такие обычные контейнерные функции, как begin(), end() и size(). Основные манипуляции над строками Чтобы присвоить одну строку другой, используйте функцию assign(). Вот как выглядят два возможных формата ее реализации. string &assign(const string &strob, size_type start, size_type num); string &assign(const char *str, size_type num); Первый формат позволяет присвоить вызывающему объекту num символов из строки, заданной параметром strob, начиная с индекса start. При использовании второго формата вызывающему объекту присваиваются первые num символов строки с завершающим нулем, заданной параметром str. В каждом случае возвращается ссылка на вызывающий объект. Конечно, гораздо проще для присвоения одной полной строки другой использовать оператор "=". О функции-члене assign() вспоминают, в основном, тогда, когда нужно присвоить только часть строки. С помощью функции-члена append() можно часть одной строки присоединить в конец другой. Два возможных формата ее реализации имеют следующий вид. string &append(const string &strob, size_type start, size_type num); string &append(const char *str, size_type num); Здесь при использовании первого формата num символов из строки, заданной параметром strob, начиная с индекса start, будет присоединено в конец вызывающего объекта. Второй формат позволяет присоединить в конец вызывающего объекта первые num символов строки с завершающим нулем, заданной параметром str. В каждом случае возвращается ссылка на вызывающий объект. Конечно, гораздо проще для присоединения одной полной строки в конец другой использовать оператор Функция же append() применяется тогда, когда необходимо присоединить в конец вызывающего объекта только часть строки. Вставку или замену символов в строке можно выполнять с помощью функций-членов insert() и replace(). Вот как выглядят прототипы их наиболее употребительных форматов. string &insert(size_type start, const string &strob); string &insert(size_type start, const string &strob, size_type insStart, size_type num); string &replace(size_type start, size_type num, const string &strob); string &replace(size_type start, size_type orgNum, const string &strob, size_type replaceStart, size_type replaceNum); Первый формат функции insert() позволяет вставить строку, заданную параметром strob, в позицию вызывающей строки, заданную параметром start. Второй формат функции insert() предназначен для вставки num символов из строки, заданной параметром strob, начиная с индекса insStart, в позицию вызывающей строки, заданную параметром start. Первый формат функции replace() служит для замены num символов в вызывающей строке, начиная с индекса start, строкой, заданной параметром strob. Второй формат позволяет заменить orgNum символов в вызывающей строке, начиная с индекса start, replaceNum символами строки, заданной параметром strob, начиная с индекса replaceStart. В каждом случае возвращается ссылка на вызывающий объект. Удалить символы из строки можно с помощью функции erase(). Один из ее форматов выглядит так: string &erase(size_type start = 0, size_type num = npos); Эта функция удаляет num символов из вызывающей строки, начиная с индекса start. Функция возвращает ссылку на вызывающий объект. Использование функций insert(), erase() и replace() демонстрируется в следующей программе. // Демонстрация использования функций insert(), erase() и replace(). #include #include using namespace std; int main() { string str1("Это простой тест."); string str2("ABCDEFG"); cout << "Исходные строки:\n"; cout << "str1: " << str1 << endl; cout << "str2: " << str2 << "\n\n"; // Демонстрируем использование функции insert(). cout << "Вставляем строку str2 в строку str1:\n"; str1.insert(5, str2); cout << str1 << "\n\n"; // Демонстрируем использование функции erase(). cout << "Удаляем 7 символов из строки str1:\n"; str1.erase(5, 7); cout << str1 <<"\n\n"; // Демонстрируем использование функции replace(). cout << "Заменяем 2 символа в str1 строкой str2:\n"; str1.replace(5, 2, str2); cout << str1 << endl; return 0; } Результаты выполнения этой программы таковы. Исходные строки: str1: Это простой тест. str2: ABCDEFG Вставляем строку str2 в строку str1: Это пABCDEFGростой тест. Удаляем 7 символов из строки str1: Это простой тест. Заменяем 2 символа в str1 строкой str2: Это пABCDEFGстой тест. Поиск в строке В классе string предусмотрено несколько функций-членов, которые осуществляют поиск. Это, например, такие функции, как find() и rfind(). Рассмотрим прототипы самых употребительных версий этих функций. size_type find(const string &strob, size_type start=0) const; size_type rfind(const string &strob, size_type start=npos) const; Функция find(), начиная с позиции start, просматривает вызывающую строку на предмет поиска первого вхождения строки, заданной параметром strob. Если поиск успешен, функция find() возвращает индекс, по которому в вызывающей строке было обнаружено совпадение. Если совпадения не обнаружено, возвращается значение npos. Функция rfind() выполняет то же действие, но с конца. Начиная с позиции start, она просматривает вызывающую строку в обратном направлении на предмет поиска первого вхождения строки, заданной параметром strob (т.е. она находит в вызывающей строке последнее вхождение строки, заданной параметром strob). Если поиск прошел удачно, функция rfind() возвращает индекс, по которому в вызывающей строке было обнаружено совпадение. Если совпадения не обнаружено, возвращается значение npos. Рассмотрим короткий пример использования функции find(). #include #include using namespace std; int main() { int i; string s1 ="Класс string облегчает обработку строк."; string s2; i = s1.find("string"); if(i != string::npos) { cout << "Совпадение обнаружено в позиции " << i<< endl; cout << "Остаток строки таков: "; s2.assign (s1, i, s1.size()); cout << s2; } return 0; } Программа генерирует такие результаты. Совпадение обнаружено в позиции 6 Остаток строки таков: string облегчает обработку строк. Сравнение строк Чтобы сравнить полное содержимое одного string-объекта с другим, обычно используются описанные выше перегруженные операторы отношений. Но если нужно сравнить часть одной строки с другой, вам придется использовать функцию-член compare(). int compare(size_type start, size_type num, const string &strob) const; Функция compare() сравнивает с вызывающей строкой num символов строки, заданной параметром strob, начиная с индекса start. Если вызывающая строка меньше строки strob, функция compare() возвратит отрицательное значение. Если вызывающая строка больше строки strob, она возвратит положительное значение. Если строка strob равна вызывающей строке, функция compare() возвратит нуль. Получение строки с завершающим нулем Несмотря на неоспоримую полезность объектов типа string, возможны ситуации, когда вам придется получать из такого объекта символьный массив с завершающим нулем, т.е. его версию С-строки. Например, вы могли бы использовать string-объект для создания имени файла. Но, открывая файл, вам нужно задать указатель на стандартную строку с завершающим нулем. Для решения этой проблемы и используется функция-член c_str(). Вот как выглядит ее прототип: const char *c_str() const; Эта функция возвращает указатель на С-версию строки (т.е. на строку с завершающим нулевым символом), которая содержится в вызывающем объекте типа string. Полученная строка с завершающим нулем изменению не подлежит. Кроме того, после выполнения других операций над этим string-объектом допустимость применения полученной С-строки не гарантируется. Хранение строк в других контейнерах Поскольку класс string определяет тип данных, можно создать контейнеры, которые будут содержать объекты типа string. Рассмотрим, например, более удачный вариант программы-словаря, которая была показана выше. /* Использование отображения string-объектов для создания словаря. */ #include #include map p = dictionary.find(s); if(p != dictionary.end()) cout << "Определение: " << p->second; else cout << "Такого слова в словаре нет.\n"; return 0; } И еще об STL Библиотека STL — важная составляющая языка C++. Многие задачи программирования можно описать, используя терминологию STL. Эта библиотека великолепно сочетает силу своих средств с гибкостью их применения. Несмотря на то что ее синтаксис немного сложноват, он быстро осваивается. Ни один уважающий себя С++-программист не может пренебречь возможностями библиотеки STL, поскольку она — не только настоящее, но и будущее С++-программирования. |