Шаг 504.
Библиотека STL.
Ввод-вывод с использованием потоковых классов. Потоковые классы char*

    На этом шаге мы рассмотрим особенности использования потоковых классов char*.

    Потоковые классы char* поддерживаются только в целях обратной совместимости. Их интерфейс иногда порождает ошибки, с этими классами часто работают неправильно. Тем не менее они продолжают широко использоваться, поэтому далее приводятся их краткие описания. Учтите, что в описываемой здесь стандартной версии прежний интерфейс был слегка изменен.

    Далее вместо термина строка будет использоваться термин последовательность символов. Дело в том, что последовательность символов, поддерживаемая потоковыми классами char*, не всегда заканчивается символом завершения строки (а следовательно, не является строкой в обычном понимании).

    Потоковые классы char* определены только для символьного типа char. К этой категории относятся следующие классы:

    Потоковые классы char* определяются в заголовочном файле <strstream>.

    Поток данных istrstream может инициализироваться последовательностью символов (типа char*), либо завершенной символом \0, либо имеющей длину, переданную в аргументе. Типичный пример чтения и обработки строк:

char buffer[1000];        // Буфер, в которой могут храниться до 999 символов 
// Чтение строки 
std::cin.get(buffer,sizeof(buffer));
// Чтение/обработка строки как потока 
std::istrstream input(buffer);
.   .   .   .   .
input >> x;

    Поток данных char*, предназначенный для записи, поддерживает последовательность символов, растущую по мере необходимости, или инициализируется буфером фиксированного размера. При помощи флагов ios::app и ios::ate можно дописывать выводимые символы к последовательности, уже хранящейся в буфере.

    При строковой интерпретации потока данных char* необходима осторожность. В отличие от строковых потоков данных потоки типа char* не всегда следят за использованием памяти, в которой хранится последовательность символов.

    Функция str() предоставляет вызывающей стороне доступ к последовательности символов вместе с ответственностью за управление соответствующей памятью. Если поток данных не был инициализирован буфером фиксированного размера (за который поток никогда не отвечает), должны соблюдаться следующие трн правила.

    Пример использования потока char*:

float x;
// Создание и заполнение потока char*
// - не забывайте о ends или '\0'!!!
std::ostrstream buffer;      // Динамический потоковый буфер 
buffer << "float х: " << х << std::ends;
// Передача полученной С-строки функции foo() и возвращение памяти в буфер
char* s = buffer.str();
foo(s);
buffer.freeze(false);

    Зафиксированный поток char* можно привести в обычное состояние для дополнительной обработки. Для этого следует вызвать функцию freeze() с аргументом false. При выполнении этой операции право владения последовательностью символов снова возвращается объекту потока данных. Это единственный безопасный способ освобождения памяти для последовательности символов. В следующем примере показано, как это делается:

float х;
std::ostrstream buffer;   // Динамический поток char*
// Заполнение потока char*
buffer << "float x: " << x << std::ends;
// Передача итоговой С-строки функции foo()
//   - фиксация потока char*
foo(buffer.str());
// Снятие фиксации с потока char* 
buffer.freeze(false);
// Установка позиции записи в начало 
buffer.seekp (0,ios::beg);
// Повторное заполнение потока char*
buffer << "once more float x: " << x << std::ends;
// Повторная передача полученной С-строки функции foo()
//  - фиксация потока char*;
foo(buffer.str());
// Возвращение памяти в буфер 
buffer.freeze(false);

    Проблем, возникающих из-за фиксации потока данных, нет в классах строковых потоков данных. В основном это объясняется тем, что использование памяти при копировании находится под контролем строкового класса.

    Со следующего шага мы начнем рассматривать операторы ввода-вывода для пользовательских типов.




Предыдущий шаг Содержание Следующий шаг