Бьерн Страуструп - Язык программирования С++. Главы 9-10
Страница 42. Буферизация



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.

 
« Предыдущая статья   Следующая статья »