Выбрать главу

while ( cin setw(sizeof( buf )) buf )

Применение оператора sizeof в следующем примере дает неожиданный результат:

#include iostream

#include iomanip

int main()

{

const int bufSize = 24;

char buf[ bufSize ];

char *pbuf = buf;

// если строка длиннее, чем sizeof(char*),

// она разбивается на несколько строк

while ( cin setw( sizeof( pbuf )) pbuf )

cout pbuf endl;

}

Программа печатает:

$ a.out

The winter of our discontent

The

win

ter

of

our

dis

con

ten

t

Функции setw() вместо размера массива передается размер указателя, длина которого на нашей машине равна четырем байтам, поэтому вывод разбит на строки по три символа.

Попытка исправить ошибку приводит к еще более серьезной проблеме:

while ( cin setw(sizeof( *pbuf )) pbuf )

Мы хотели передать setw() размер массива, адресуемого pbuf. Но выражение

*pbuf

дает только один символ, т.е. объект типа char. Поэтому setw() передается значение 1. На каждой итерации цикла while в массив, на который указывает pbuf, помещается только нулевой символ. До чтения из стандартного ввода дело так и не доходит, программа зацикливается.

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

#include iostream.h

#include string

int main()

{

string buf, largest;

// для хранения статистики

int curLen, // длина текущего слова

max = -1, // максимальная длина слова

cnt = 0; // счетчик прочитанных слов

while ( cin buf )

{

curLen = buf.size();

++cnt;

// новое самое длинное слово? сохраним его

if ( curLen max )

{

max = curLen;

largest = buf;

}

}

cout "Число прочитанных слов " cnt endl;

cout "Длина самого длинного слова " max endl;

cout "Самое длинное слово " largest endl;

}

Однако запятая и кавычка по-прежнему считаются частью слова. Напишем функцию для удаления этих символов из слова:

#include string

void filter_string( string &str )

{

// элементы, подлежащие фильтрации

string filt_elems( "\",?." );

string::size_type pos = 0;

while (( pos = str.find_first_of( filt_elems, pos ))

!= string::npos )

str.erase( pos, 1 );

}

Эта функция работает правильно, но множество символов, которые мы собираемся отбрасывать, "зашито" в код. Лучше дать пользователю возможность самому передать строку, содержащую такие символы. Если он согласен на множество по умолчанию, то может передать пустую строку.

#include string

void filter_string( string &str,

string filt_elems = string("\",."))

{

string::size_type pos = 0;

while (( pos = str.find_first_of( filt_elems, pos ))

!= string::npos )

str.erase( pos, 1 );

}

Более общая версия filter_string() принимает пару итераторов, обозначающих диапазон, где производится фильтрация:

template class InputIterator

void filter_string( InputIterator first, InputIterator last,

string filt_elems = string("\",."))

{

for ( ; first != last; first++ )

{

string::size_type pos = 0;

while (( pos = (*first).find_first_of( filt_elems, pos ))

!= string::npos )

(*first).erase( pos, 1 );

}

}

С использованием этой функции программа будет выглядеть так:

#include string

#include algorithm

#include iterator

#include vector

#include iostream

bool length_less( string s1, string s2 )

{ return s1.size() s2.size(); }

int main()

{

istream_iterator string input( cin ), eos;

vector string text;

// copy - это обобщенный алгоритм

copy( input, eos, back_inserter( text ));

string filt_elems( "\",.;:");

filter_string( text.begin(), text.end(), filt_elems );

int cnt = text.size();

// max_element - это обобщенный алгоритм

string *max = max_element( text.begin(), text.end(),

length_less );

int len = max-size();

cout "Число прочитанных слов "

cnt endl;

cout "Длина самого длинного слова "

len endl;

cout "Самое длинное слово "

*max endl;

}

Когда мы применили в алгоритме max_element() стандартный оператор "меньше", определенный в классе string, то были удивлены полученным результатом: