Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Бьерн Страуструп C++.doc
Скачиваний:
12
Добавлен:
07.11.2018
Размер:
2.45 Mб
Скачать

10.5.2 Строковые потоки

Как было показано, поток может быть привязан к файлу, т.е. массиву символов, хранящемуся не в основной памяти, а, например, на диске. Точно так же поток можно привязать к массиву символов в основной памяти. Например, можно воспользоваться выходным строковым потоком ostrstream для форматирования сообщений, не подлежащих немедленной печати:

char* p = new char[message_size];

ostrstream ost(p,message_size);

do_something(arguments,ost);

display(p);

С помощью стандартных операций вывода функция do_something может писать в поток ost, передавать ost подчиняющимся ей функциям и т.п. Контроль переполнения не нужен, поскольку ost знает свой размер и при заполнении перейдет в состояние, определяемое fail(). Затем функция display может послать сообщение в "настоящий" выходной поток. Такой прием наиболее подходит в тех случаях, когда окончательная операция вывода предназначена для записи на более сложное устройство, чем традиционное, ориентированное на последовательность строк, выводное устройство. Например, текст из ost может быть помещен в фиксированную область на экране.

Аналогично, istrstream является вводным строковым потоком, читающим из последовательности символов, заканчивающейся нулем:

void word_per_line(char v[], int sz)

/*

печатать "v" размером "sz" по одному слову в строке

*/

{

istrstream ist(v,sz); // создать istream для v

char b2[MAX]; // длиннее самого длинного слова

while (ist>>b2) cout <<b2 << "\n";

}

Завершающий нуль считается концом файла.

Строковые потоки описаны в файле <strstream.h>.

10.5.3 Буферизация

Все операции ввода-вывода были определены без всякой связи с типом файла, но нельзя одинаково работать со всеми устройствами без учета алгоритма буферизации. Очевидно, что потоку ostream, привязанному к строке символов, нужен не такой буфер, как ostream, привязанному к файлу. Такие вопросы решаются созданием во время инициализации разных буферов для потоков разных типов. Но существует только один набор операций над этими типами буферов, поэтому в ostream нет функций, код которых учитывает различие буферов. Однако, функции, следящие за переполнением и обращением к пустому буферу, являются виртуальными. Это хороший пример применения виртуальных функций для единообразной работы с эквивалентными логически, но различно реализованными структурами, и они вполне справляются с требуемыми алгоритмами буферизации. Описание буфера потока в файле <iostream.h> может выглядеть следующим образом:

class streambuf { // управление буфером потока

protected:

char* base; // начало буфера

char* pptr; // следующий свободный байт

char* gptr; // следующий заполненный байт

char* eptr; // один из указателей на конец буфера

char alloc; // буфер, размещенный с помощью "new"

//...

// Опустошить буфер:

// Вернуть EOF при ошибке, 0 - удача

virtual int overflow(int c = EOF);

// Заполнить буфер:

// Вернуть EOF в случае ошибки или конца входного потока,

// иначе вернуть очередной символ

virtual int underflow();

//...

public:

streambuf();

streambuf(char* p, int l);

virtual ~streambuf();

int snextc() // получить очередной символ

{

return (++gptr==pptr) ? underflow() : *gptr&0377;

}

int allocate(); // отвести память под буфер

//...

};

Подробности реализации класса streambuf приведены здесь только для полноты представления. Не предполагается, что есть общедоступные реализации, использующие именно эти имена. Обратите внимание на определенные здесь указатели, управляющие буфером; с их помощью простые посимвольные операции с потоком можно определить максимально эффективно (и причем однократно) как функции-подстановки. Только функции overflow() и underflow() требует своей реализации для каждого алгоритма буферизации, например:

class filebuf : public streambuf {

protected:

int fd; // дескриптор файла

char opened; // признак открытия файла

public:

filebuf() { opened = 0; }

filebuf(int nfd, char* p, int l)

: streambuf(p,l) { /* ... */ }

~filebuf() { close(); }

int overflow(int c=EOF);

int underflow();

filebuf* open(char *name, ios::open_mode om);

int close() { /* ... */ }

//...

};

int filebuf::underflow() // заполнить буфер из "fd"

{

if (!opened || allocate()==EOF) return EOF;

int count = read(fd, base, eptr-base);

if (count < 1) return EOF;

gptr = base;

pptr = base + count;

return *gptr & 0377; // &0377 предотвращает размножение знака

}

За дальнейшими подробностями обратитесь к руководству по реализации класса streambuf.