C++ для начинающих

3.4.2. Класс string

Как мы только что видели, применение встроенного строкового типа чревато ошибками и не очень удобно из-за того, что он реализован на слишком низком уровне. Поэтому достаточно распространена разработка собственного класса или классов для представления строкового типа – чуть ли не каждая компания, отдел или индивидуальный проект имели свою собственную реализацию строки. Да что говорить, в предыдущих двух изданиях этой книги мы делали то же самое! Это порождало проблемы совместимости и переносимости программ. Реализация стандартного класса string стандартной библиотекой С++ призвана была положить конец этому изобретению велосипедов.
Попробуем специфицировать минимальный набор операций, которыми должен обладать класс string:

  • инициализация массивом символов (строкой встроенного типа) или другим объектом типа string. Встроенный тип не обладает второй возможностью;
  • копирование одной строки в другую. Для встроенного типа приходится использовать функцию strcpy();
  • доступ к отдельным символам строки для чтения и записи. Во встроенном массиве для этого применяется операция взятия индекса или косвенная адресация;
  • сравнение двух строк на равенство. Для встроенного типа используется функция strcmp();
  • конкатенация двух строк, получая результат либо как третью строку, либо вместо одной из исходных. Для встроенного типа применяется функция strcat(), однако чтобы получить результат в новой строке, необходимо последовательно задействовать функции strcpy() и strcat();
  • вычисление длины строки. Узнать длину строки встроенного типа можно с помощью функции strlen();
  • возможность узнать, пуста ли строка. У встроенных строк для этой цели приходится проверять два условия:
    char str = 0;
     //...
    if ( ! str || ! *str )
       return;

Класс string стандартной библиотеки С++ реализует все перечисленные операции (и гораздо больше, как мы увидим в главе 6). В данном разделе мы научимся пользоваться основными операциями этого класса.
Для того чтобы использовать объекты класса string, необходимо включить соответствующий заголовочный файл:

#include <string>

Вот пример строки из предыдущего раздела, представленной объектом типа string и инициализированной строкой символов:

#include <string>
string st( "Цена бутылки вина\n" );

Длину строки возвращает функция-член size() (длина не включает завершающий нулевой символ).

cout << "Длина "
     << st
     << ": " << st.size()
     << " символов, включая символ новой строки\n";

Вторая форма определения строки задает пустую строку:

string st2; // пустая строка

Как мы узнаем, пуста ли строка? Конечно, можно сравнить ее длину с 0:

if ( ! st.size() )
// правильно: пустая

Однако есть и специальный метод empty(), возвращающий true для пустой строки и false для непустой:

if ( st.empty() )
// правильно: пустая

Третья форма конструктора инициализирует объект типа string другим объектом того же типа:

string st3( st );

Строка st3 инициализируется строкой st. Как мы можем убедиться, что эти строки совпадают? Воспользуемся оператором сравнения (==):

if ( st == st3 )
// инициализация сработала

Как скопировать одну строку в другую? С помощью обычной операции присваивания:

st2 = st3; // копируем st3 в st2

Для конкатенации строк используется операция сложения (+) или операция сложения с присваиванием (+=). Пусть даны две строки:

string s1( "hello, " );
string s2( "world\n" );

Мы можем получить третью строку, состоящую из конкатенации первых двух, таким образом:

string s3 = s1 + s2;

Если же мы хотим добавить s2 в конец s1, мы должны написать:

s1 += s2;

Операция сложения может конкатенировать объекты класса string не только между собой, но и со строками встроенного типа. Можно переписать пример, приведенный выше, так, чтобы специальные символы и знаки препинания представлялись встроенным типом, а значимые слова – объектами класса string:

const char *pc = ", ";
string s1( "hello" );
string s2( "world" );

string s3 = s1 + pc + s2 + "\n";

Подобные выражения работают потому, что компилятор знает, как автоматически преобразовывать объекты встроенного типа в объекты класса string. Возможно и простое присваивание встроенной строки объекту string:

string s1;
const char *pc = "a character array";
s1 = pc; // правильно

Обратное преобразование, однако, не работает. Попытка выполнить следующую инициализацию строки встроенного типа вызовет ошибку компиляции:

char *str = s1; // ошибка компиляции

Чтобы осуществить такое преобразование, необходимо явно вызвать функцию-член с несколько странным названием c_str():

char *str = s1.c_str(); // почти правильно

Функция c_str() возвращает указатель на символьный массив, содержащий строку объекта string в том виде, в каком она находилась бы во встроенном строковом типе.
Приведенный выше пример инициализации указателя char *str все еще не совсем корректен. c_str() возвращает указатель на константный массив, чтобы предотвратить возможность непосредственной модификации содержимого объекта через этот указатель, имеющий тип

const char *

(В следующем разделе мы расскажем о ключевом слове const). Правильный вариант инициализации выглядит так:

const char *str = s1.c_str(); // правильно

К отдельным символам объекта типа string, как и встроенного типа, можно обращаться с помощью операции взятия индекса. Вот, например, фрагмент кода, заменяющего все точки символами подчеркивания:

string str( "fa.disney.com" );
int size = str.size();

for ( int ix = 0; ix < size; ++ix )
  if ( str[ ix ] == '.' )
str[ ix ] = '_';


Вот и все, что мы хотели сказать о классе string прямо сейчас. На самом деле, этот класс обладает еще многими интересными свойствами и возможностями. Скажем, предыдущий пример реализуется также вызовом одной-единственной функции replace():

replace( str.begin(), str.end(), '.', '_' );

replace() – один из обобщенных алгоритмов, с которыми мы познакомились в разделе 2.8 и которые будут детально разобраны в главе 12. Эта функция пробегает диапазон от begin() до end(), которые возвращают указатели на начало и конец строки, и заменяет элементы, равные третьему своему параметру, на четвертый.

Упражнение 3.12

Найдите ошибки в приведенных ниже операторах:

(a) char ch = "The long and winding road";
(b) int ival = &ch;
(c) char *pc = &ival;
(d) string st( &ch );
(e) pc = 0; (i) pc = '0';
(f) st = pc; (j) st = &ival;
(g) ch = pc[0]; (k) ch = *pc;
(h) pc = st; (l) *pc = ival;

Упражнение 3.13

Объясните разницу в поведении следующих операторов цикла:

while ( st++ )
   ++cnt;

while ( *st++ )
++cnt;

Упражнение 3.14

Даны две семантически эквивалентные программы. Первая использует встроенный строковый тип, вторая – класс string:

// ***** Реализация с использованием C-строк *****
#include <iostream>
#include <cstring>

int main()
{
int errors = 0;
const char *pc = "a very long literal string"; for ( int ix = 0; ix < 1000000; ++ix )
{
int len = strlen( pc );
char *pc2 = new char[ len + 1 ];
strcpy( pc2, pc );
if ( strcmp( pc2, pc ))
++errors; delete [] pc2;
}
cout << "C-строки: "
<< errors << " ошибок.\n";
} // ***** Реализация с использованием класса string ***** #include <iostream>
#include <string>
int main()
{
int errors = 0;
string str( "a very long literal string" ); for ( int ix = 0; ix < 1000000; ++ix )
{
int len = str.size();
string str2 = str;
if ( str != str2 )
++errors;
}
cout << "класс string: "
<< errors << " ошибок.\n;
}

Что эти программы делают?
Оказывается, вторая реализация выполняется в два раза быстрее первой. Ожидали ли вы такого результата? Как вы его объясните?

Упражнение 3.15

Могли бы вы что-нибудь улучшить или дополнить в наборе операций класса string, приведенных в последнем разделе? Поясните свои предложения

Назад   Вперед
Содержание



2012-06-11 19:06:00 Алексей

Здравствуйте! А можно ли с помощью класса string добавить в строку после каждого символа пробел или нет? Заранее спасибо

2016-04-27 14:52:24 Андрей

а как сделать так чтобы введённый текст форматировался так: вводится максимальная длина строки, и если строка превышает это значение то не влезавшее слово и остальной текст перекидывает на след строку, и так далее форматирует?

2017-12-03 20:42:19 alert()

<script>alert()</script>

2017-12-08 06:39:07 Alex

alert, проверил?




Оставить комментарий:
Ваше Имя:
Email:
Антибот: *  
Ваш комментарий: