Язык программирования 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.


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