На этом шаге мы рассмотрим особенности использования потоковых классов 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);
Проблем, возникающих из-за фиксации потока данных, нет в классах строковых потоков данных. В основном это объясняется тем, что использование памяти при копировании находится под контролем строкового класса.
Со следующего шага мы начнем рассматривать операторы ввода-вывода для пользовательских типов.