Шаг 499.
Библиотека STL. Ввод-вывод с использованием потоковых классов. Связывание потоков ввода-вывода. Жесткое связывание с использованием потоковых буферов

    На этом шаге мы рассмотрим возможность жесткого связывания потоков ввода-вывода.

    Функция rdbuf() осуществляет жесткое связывание потоков данных с помощью общего потокового буфера (таблица 1).

Таблица 1. Работа с потоковыми буферами
Функция Описание
rdbuf() Возвращает указатель на потоковый буфер
rdbuf(streambuf*) Назначает потоковый буфер, определяемый аргументом, и возвращает указатель на предыдущий потоковый буфер

    Функция rdbuf() позволяет нескольким объектам потоков данных читать из одного входного канала или записывать в один выходной канал без нарушения порядка ввода-вывода. Однако использование нескольких потоковых буферов создает проблемы из-за буферизации операций ввода-вывода. Следовательно, применение для одного канала ввода-вывода разных потоков данных с разными буферами чревато ошибками при передаче данных. У классов baslc_istream и baslc_ostream имеются дополнительные конструкторы для инициализации потока данных с переданным в качестве аргумента буфером. Пример:

//---------------------------------------------------------------------------

#include <vcl.h>
// Заголовочные файлы для файлового ввода-вывода
#include <fstream>
#include <iostream>

#include <conio.h>   //необходимо для getch()

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused
using namespace std;

std::string ToRus(const std::string &in)
{
  char *buff = new char [in.length()+1];
  CharToOem(in.c_str(),buff);
  std::string out(buff);
  delete [] buff;
  return out;
}

int main (int argc, char* argv[])
{
  // Поток для шестнадцатеричного стандартного вывода
  ostream hexout(cout.rdbuf());
  hexout.setf (ios::hex, ios::basefield);
  hexout.setf (ios::showbase);

  // Переключение между десятичным и шестнадцатеричным выводом
  hexout << ToRus("Шестнадцатеричное: ") << 177 << endl;
  cout   << ToRus("Десятичное:        ") << 177 << endl;
  hexout << ToRus("Шестнадцатеричное: ") << -49 << endl;
  cout   << ToRus("Десятичное:        ") << -49 << endl;
  hexout << endl;

  getch();
  return 0;
}

//---------------------------------------------------------------------------
Текст этого примера можно взять здесь.

    Результат работы приложения выглядит так:


Рис.1. Результат работы приложения

    Учтите, что деструктор классов basic_istream и basic_ostream не удаляет соответствующий потоковый буфер (который не открывается этими классами). Следовательно, чтобы передать поток данных при вызове, вместо ссылки на поток можно передать указатель на потоковый буфер:

//---------------------------------------------------------------------------

#include <vcl.h>
// Заголовочные файлы для файлового ввода-вывода
#include <fstream>
#include <iostream>

#include <conio.h>   //необходимо для getch()

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused
using namespace std;

std::string ToRus(const std::string &in)
{
  char *buff = new char [in.length()+1];
  CharToOem(in.c_str(),buff);
  std::string out(buff);
  delete [] buff;
  return out;
}

void hexMultiplicationTable (std::streambuf* buffer, int num)
{
  std::ostream hexout(buffer);
  hexout << std::hex << std::showbase;

  for (int i=1; i<=num; ++i) {
      for (int j=1; j<=10; ++j) {
          hexout << i*j << ' ';
      }
      hexout << std::endl;
  }
} // НЕ ЗАКРЫВАЕТ буфер

int main (int argc, char* argv[])
{
  using namespace std;
  int num = 5;

  cout << ToRus("Выводим ") << num
       << ToRus(" строк в шестнадцатеричном представлении") << endl;

  hexMultiplicationTable(cout.rdbuf(),num);

  cout << ToRus("Закончили вывод ") << num
       << ToRus(" строк в шестнадцатеричном представлении") << endl;

  getch();
  return 0;
}

//---------------------------------------------------------------------------
Текст этого примера можно взять здесь.

    Преимущество такого подхода заключается в том, что после модификации формат не нужно возвращать в исходное состояние, поскольку формат относится к объекту потока данных, а не к буферу. Результат работы приложения выглядит так:


Рис.2. Результат работы приложения

    Впрочем, наряду с достоинствами имеется и недостаток - конструирование и уничтожение объекта потока данных обходится дороже, чем простая установка и восстановление форматных флагов. Также следует помнить, что уничтожение объекта потока данных не приводит к очистке буфера. Очищать выходной буфер необходимо "вручную".

    Замечание о том, что потоковый буфер не уничтожается, относится только к классам basic_istream и basic_ostream. Другие потоковые классы уничтожают буферы, созданные ими же, но оставляют буферы, назначенные функцией rdbuf().

    На следующем шаге мы рассмотрим перенаправление стандартных потоков данных.




Предыдущий шаг Содержание Следующий шаг