Лекция №12: Стандартный класс string
В лекции 11 вы познакомились со строками С и узнали, что это просто массивы символов, в которых конец строки отмечается символом ' \0'. При работе с такими строками необходимо учитывать их структуру. Скажем, если в конец строки нужно добавить символы, а в массиве для них недостаточно места, приходится создавать новый массив, вмещающий строки большей длины. Короче говоря, работая со строками С, нужно помнить об особенностях их реализации и хранения в памяти. Это является источником лишней работы и часто приводит к ошибкам. Стандарт C++ ANSI/ISO определяет, что в языке должен быть задан класс string, позволяющий работать со строками как с базовым типом данных и не заботиться о деталях его реализации. С этим типом данных вы сейчас и познакомитесь.
Первое знакомство с классом string
Класс string определен в библиотеке с именем <string> и отнесен к пространству имен std. Поэтому для его использования в программу нужно включить следующие директивы:
#include <string>
using namespace std;
Класс string позволяет работать со значениями и выражениями типа string почти так же, как со значениями простого типа данных. Так, для присваивания значения переменной типа string можно пользоваться оператором =, а для конкатенации двух строк — оператором +. Предположим, что s1, s2 и s3 являются объектами типа string и переменные s1 и s2 содержат строковые значения. Тогда переменной s3 можно присвоить строку, состоящую из s1, в конец которой добавлена строка s2, при помощи оператора:
s3 = s1 + s2;
выполняющего присваивание и конкатенацию.
И при этом нет риска, что s3 окажется слишком маленькой для нового значения. Если сумма значений длины строк si и s2 превысит вместимость переменной s3, для нее будет автоматически выделена дополнительная память.
Как отмечалось ранее в этой главе, заключенные в двойные кавычки строковые литералы в программе на C++ фактически являются строками С, и поэтому они не относятся к типу string. Но ими можно пользоваться как литеральными значениями типа string, и мы часто будем говорить о них как о значениях типа string. Так, оператор
s3 = "Hello Mom!";
присваивает переменной s3 объект string, содержащий те же символы и в том же порядке, что и строка С "Hello Mom!".
У класса string имеется используемый по умолчанию конструктор, инициализирующий объект типа string пустой строкой. Кроме того, у него есть второй конструктор с одним аргументом, в котором задается стандартная строка С. Этот второй конструктор инициализирует объект типа string значением, представляющим ту же строку, что и аргумент. В качестве примера рассмотрим следующие две строки:
string phrase;
string noun("ants");
В первой из них объявляется строковая переменная phrase, инициализируемая пустой строкой. Во второй объявляется строковая переменная noun, которая инициализируется строкой, подобной строке С "ants". Большинство программистов скажут, что переменная noun инициализируется строкой "ants", но это неверно. На самом деле здесь имеет место приведение типов. Заключенная в кавычки строка "ants" является строкой С, а не значением типа string. Переменной noun присваивается объект типа string, содержащий те же символы и в том же порядке, что и строка "ants", но его значение не завершается нуль-символом ' \0'. И теоретически программисту вообще не нужно знать, хранятся в нем строки в виде массивов или в виде какой-нибудь другой структуры данных.
Существует альтернативный синтаксис объявления переменной типа string и вызова конструктора этого типа. Приведенные ниже две строки:
string noun("ants");
string noun = "ants";
являются эквивалентными.
Все эти особенности класса string демонстрирует маленькая программа, приведенная в листинге 15.1. Обратите внимание, что значения типа string можно выводить с помощью оператора <<.
Листинг 15.1. Программа с использованием класса string
// Демонстрирует стандартный класс string. #include <iostream>
#include <string>
using namespace std;
Int main()
{
string phrase; // Инициализируется пустой строкой.
// Два способа инициализации строковой переменной.
string adjective("fried"), noun("ants");
string wish = "Bon appetite!";
phrase = "I love " + adjective + " " + noun + "!";
cout << phrase << endl << wish <<endl;
return 0; }
Пример диалога
I love fried ants!
Bon appetite!
Рассмотрим следующую строку из программы листинга 15.1:
phrase = "I love " + adjective + " " + noun + "!";
Для того чтобы программист мог выполнять конкатенацию строк таким простым и естественным способом, разработчикам языка C++ пришлось немало потрудиться. Строковая константа "I love " не является объектом типа string. Она хранится как строка С (в виде массива символов с нуль-терминатором). Встретив строку "I love " в качестве аргумента оператора +, C++ находит его перегруженное определение для таких значений как "I love ". Существует несколько подобных перегруженных версий этого оператора: со строкой С слева и объектом string справа, со строкой С справа и объектом типа string слева, со строками С с обеих сторон (также возвращающая объект string). И конечно, есть версия с двумя объектами типа string.
На самом деле наличие такого количества перегруженных версий оператора конкатенации в языке не обязательно. Если бы их не было, C++ просто вызывал бы конструктор класса string, преобразующий строку С "I love " в объект типа string, к которому можно применить оператор +. Это преобразование выполняет конструктор с одним строковым параметром. Но наличие полного набора перегруженных версий оператора + позволяет выполнять конкатенацию более эффективно.
Класс string
Класс string может использоваться для представления строк символов, причем более гибкого и эффективного, чем строки С.
Данный класс определен в библиотеке string, и это определение отнесено к пространству имен std. Поэтому программы, в которых он применяется, должны содержать следующие (или эквивалентные им) директивы:
#include <string>
using namespace std;
У класса string имеется используемый по умолчанию конструктор, инициализирующий объект типа string пустой строкой, и конструктор, который принимает в качестве аргумента строку С и инициализирует объект типа string представленным ею значением. Например:
string sis2("Hello");
После появления класса string некоторые программисты считают, что следует использовать только его. Однако на самом деле обойтись без строк С в программах на C++ непросто.
Ввод-вывод с помощью класса string
Для вывода объектов типа string можно пользоваться оператором «, точно так же, как для вывода данных других типов. Эту простую операцию демонстрирует программа, приведенная в листинге 15.1. А вот в отношении ввода данных класса такого типа имеется одна тонкость.
Оператор ввода >> для объектов типа string работает так же, как для других данных, но помните, что он игнорирует начальные пробелы и прекращает чтение, как только встретит символ пробела, табуляции или перевода строки. В качестве примера рассмотрим следующий код:
string s1, s2;
cin >> s1;
cin >> s2;
Если пользователь введет
May the hair on your toes grow long and curly!
объекту s1 будет присвоена строка "May" без ведущих и завершающих пробелов, а переменой s2 будет присвоена строка "the". To есть, оператор ввода позволяет считывать только слова, но не строки, содержащие пробелы. Иногда именно это и требуется, но в общем случае считывания одних лишь слов недостаточно.
Если нужно, чтобы программа прочитала всю введенную пользователем строку в переменную типа string, можно воспользоваться функцией getline. Синтаксис ее использования со строковыми объектами несколько отличается от синтаксиса, описанного в лекции 14 для строк С. В частности, для ввода с клавиатуры не применяется вызов cin.getline, вместо этого вызывается функция getline (не являющаяся функцией-членом объекта cin) и объект cin передается ей в качестве аргумента.
string line;
cout << "Enter a line of input:\n";
getline(cin. line);
cout << line << "END OF OUTPUT\n";
Включив в состав завершенной программы этот код, можно получить следующий диалог:
Enter some input:
Do be do to you!
Do be do to you!END OF OUTPUT
Если в строке имеются ведущие или завершающие пробелы, они также становятся частью строкового значения, прочитанного функцией getline. Данная версия getline находится в библиотеке string. Вместо cin ей можно передавать потоковый объект, подключенный к текстовому файлу.
Ввод-вывод данных объектов типа string
Для вывода данных в объект типа string можно пользоваться оператором << и объектом cout, а для ввода — оператором >> и объектом cin. Однако при использовании оператора >> для ввода данных в объект типа string считывается только часть строки, ограниченная символами пробела, табуляции или перевода строки. Чтобы ввести всю строку с клавиатуры, можно применять функцию getline.
Примеры
string greeting("Hello"), response, next_word;
cout << greeting << endl;
getline(cin.response);
cin >> next_word;
Для чтения пустого символа нельзя использовать сin и >>.
Посимвольное чтение строки можно выполнить с помощью функции cin.get. Она считывает значения типа char, а не string, но может быть полезна для ввода данных в объект string. В листинге 15.2 приведена программа, демонстрирующая использование функций getline и cin.get для ввода данных в объект типа string. Назначение функции newline объясняется в вопросе: одновременное использование потока cin с оператором >> и функции getline этой лекции.
Листинг 15.2. Программа, использующая класс string
// Демонстрирует использование функций getline и cin.get.
#include <iostream>
#include <string>