Язык программирования C++ для профессионалов

       

Буферизация


Все операции ввода-вывода были определены без всякой связи с типом файла, но нельзя одинаково работать со всеми устройствами без учета алгоритма буферизации. Очевидно, что потоку 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.



Содержание раздела