Стандартные функции ввода-вывода. Принципы работы манипуляторов

   
На этом шаге мы рассмотрим общие принципы работы манипуляторов.

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

  ostream& ostream::operator << (ostream (*op)(ostream&))
  {
    // Вызов функцию, передаваемой в параметре, с аргументом-потоком 
    return (*op)(*this); 
  }

   
Аргумент op представляет собой указатель на функцию. Точнее говоря, это функция, которая получает и возвращает поток данных ostream
(предполагается, что возвращается тот же объект ostream, который был получен при вызове). Если второй операнд оператора << является такой функцией,
то при ее вызове в аргументе передается первый операнд оператора <<.

   
На первый взгляд описание кажется очень сложным, но в действительности все относительно просто. Следующий пример поможет лучше разобраться в происходящем.
Манипулятор (то есть функция) endl() для объекта ostream реализуется примерно так:

  std::ostream& std::endl (std::ostream& strm)
  {
    // Запись признака конца строки 
    strm.put('\n');
    // Принудительный вывод выходного буфера 
    strm.flush();
    // Возвращение strm для организации цепочечных вызовов 
    return strm; 
  }

   
Манипулятор используется в выражениях вида

  std::cout << std::endl;

   
Для потока данных cout вызывается оператор <<, которому во втором операнде передается функция endl(). Реализация оператора <<
преобразует этот вызов в вызов переданной функции, которой в качестве аргумента передается объект потока данных:

  std::endl (std::cout)

   
Чтобы добиться эффекта "вывода" манипулятора, можно просто использовать это выражение. Более того, у функциональной записи есть свои преимущества - она не
требует указания пространства имен:

  endl(std::cout)

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

   
Поскольку потоковые классы оформлены в виде шаблонов, параметризованных по типу символов, настоящая реализация endl() выглядит примерно так:

  tempiate<class charT, class traits>
  std::basic_ostream<charT,traits>&
  std::endl (std::basic_ostream<charT,traits>& strm)
  {
    strm.put(strm.widen('\n'));
    strm.flush();
    return strm; 
  }

   
Функция widen() преобразует символ новой строки к кодировке, используемой в потоке данных.

   
В стандартную библиотеку C++ также включены параметризованные манипуляторы (то есть манипуляторы, которым при вызове передаются аргументы).
Принципы работы этих манипуляторов зависят от реализации; не существует стандартных правил определения пользовательских параметризованных манипуляторов.

   
Стандартные параметризованные манипуляторы определяются в заголовоч-ном файле <iomanip>. Если вы собираетесь использовать их, в программу
необходимо включить соответствующий файл:

  #include <iomanip>

   
Все стандартные параметризованные манипуляторы связаны с форматированием данных, поэтому они будут рассматриваться далее при описании средств
форматирования.

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



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий