Шаг 505.
Библиотека STL. Ввод-вывод с использованием потоковых классов. Операторы ввода-вывода для пользовательских типов. Реализация операторов вывода

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

    Как упоминалось ранее, главным преимуществом потокового ввода-вывода перед средствами ввода-вывода языка С является возможность расширения потокового механизма для пользовательских типов. Расширение основано на перегрузке операторов << и >>. Далее рассматривается пример использования потоков данных для вывода правильных дробей.

Реализация операторов вывода

    В выражении с оператором вывода << левый операнд определяет поток данных, а правый - объект, записываемый в этот поток:

  поток << объект

    В соответствии с правилами языка эта конструкция может интерпретироваться двумя способами:

  поток.operator<<(объект) 
  поток.operator<<(поток, объект)

    Первая интерпретация используется для встроенных типов. Для пользовательских типов должна использоваться вторая интерпретация, поскольку потоковые классы закрыты для расширения. Все, что требуется, - реализовать глобальный оператор << для пользовательских типов. Задача решается относительно просто, если при этом не нужен доступ к закрытым членам объекта (но об этом позже).

    Например, для вывода объекта класса Fraction в формате числитель/знаменатель можно воспользоваться следующей функцией:

#include <iostream>
inline
std::ostream& operator << (std::ostream& strm, const Fraction& f)
{
  strm << f.numerator() << '/' << f.denominator();
  return strm; 
}

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

    У этой простой формы есть два основных недостатка.

    Эта программа выведет следующий результат:

  VAT: "16     /100"

    В следующей версии решены обе проблемы:

#include <iostream>
#include <sstream>

template <class charT, class traits>
inline
std::basic_ostream<charT,traits>&
operator << (std::basic_ostream<charT,traits>& strm,
             const Fraction& f)
{
    // Строковый поток
    //  - с тем же форматом
    //  - без специальной ширины поля
    std::basic_ostringstream<charT,traits> s;
    s.copyfmt(strm);
    s.width(0);

    // Заполнение строкового потока
    s << f.numerator() << '/' << f.denominator();

    // print string stream
    strm << s.str();

    return strm;
}

    Оператор превратился в шаблон функции, параметризованный для всех разновидностей потоков данных. Проблема с шириной поля решается записью в строковый поток данных без указания конкретной ширины. Сконструированная строка затем передается в поток данных, переданный в аргументе. В результате символьное представление дроби выводится одной операцией записи, к которой применяется ширина поля. Например, рассмотрим такой фрагмент:

Fraction vat(16,100);    // В Германии действует единая ставка НДС=16% 
std::cout << "VAT: \"" << std::left << std::setw(8) 
    << VAT << "\"" << std::endl;

    Этот фрагмент выведет следующий результат:

VAT: "16/100  "

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




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