Шаг 8.
Перегрузка операции вывода

    На этом шаге мы начнем рассматривать перегрузку операций ввода-вывода.

    При передачах данных базовых типов потоки cin, cout "знают", как выполнять преобразования значений разных типов. Напомним, что при перегрузке функций конкретная реализация выбирается в соответствии с сигнатурой, т.е. зависит от фактических параметров. Подобным образом в выражениях:

    cin >> операнд 
          и 
    cout << операнд 

каждому типу правого операнда соответствует свое правило преобразования данных. Эти правила заранее оформлены в виде процедур (функций) специального вида, называемых операциями-функциями. Определения этих функций находятся в библиотеке ввода-вывода, а прототипы размещены в заголовочном файле iostream.h.

    Чтобы использовать операции обмена << и >> с данными производных типов, определяемых пользователем, необходимо расширить действие указанных операций, введя новые операции-функции. Каждая из операций обмена << и >> бинарная, причем левым операндом служит объект, связанный с потоком, а правый операнд должен иметь желаемый тип. Этот бинарный характер операций обмена отражает спецификация параметров соответствующих операций-функций. Первый параметр - ссылка на объект потокового класса (тип istream& либо ostream&), второй параметр - ссылка или объект желаемого типа. Тип возвращаемого значения должен быть ссылкой на тот поток, для которого предназначена операция. Таким образом, формат операции-функции для перегрузки операций таков:

ostream& operator << (ostream& out, новый_тип имя) 
{ 
   . . .       // Любые операторы для параметра нового_типа. 
   out <<...    // Вывод значений нового_типа. 
   return out; // Возврат ссылки на объект класса ostream. 
}

    Здесь новый_тип - тип, определенный пользователем. Таким образом, если определить структурный тип (класс):

struct point   //  Точка  трехмерного евклидова пространства.
{  
    float  x; // Декартовы координаты точки.
    float у;
    float  z;
}

то для типа point можно определить правила вывода (по умолчанию на экран дисплея) значений, например, с помощью такой операции-функции:

ostream&   //  Тип возвращаемого  значения.
    operator <<   (ostream&  t,   point  d)
{   return  t << "\nx  = " << d.x << "  у = " << d.y << "  z  = " << d.z; }

    Отметим, что в теле функции с "названием" operator <<, операция << используется в первом бинарном выражении с объектом t типа ostream& в качестве левого операнда. Правыми операндами для каждой из операций << служат значения стандартных (базовых) типов - символьные строки и вещественные числа типа float. Результат операции:

    t << "\nx = " 

    - ссылка на объект типа ostream. Эта ссылка используется в качестве левого операнда при выводе << d.х и т.д. Так как d - имя формального параметра структурного типа point, то для обращения к компонентам структуры (х, у, z) используются уточненные имена d.x, d.y, d.z. В результате выполнения всей цепочки операций вывода в теле операции-функции формируется ссылка на объект типа ostream, и именно это значение ссылки возвращает в точку вызова оператор return. Тем самым операция включения в поток <<, применяемая в вызывающей программе к операнду типа point, вернет ссылку на выходной поток. Таким образом, сохраняется возможность применения операции << несколько раз в одном выражении, как это разрешено для операндов базовых типов (т.е. допустимы цепочки вывода). Например, если, определив структуру point и введя для нее приведенную выше операцию-функцию operator << (), выполнить программу:

void main()
{ 
    point F = { 10.0, 20.0, 30.0 };
    cout <<  "\nКоординаты точки: " << F;
}
то на экране дисплея получим:
    Координаты точки:
    х = 10.0   у = 20.0   z = 30.0

    Как видите, в данном примере при выводе в поток cout равноправно используются и вывод значения базового типа char [], и вывод значений объекта F типа point, определенного пользователем.

    Напомним, что класс ostream, поток (объект) cout и "стандартные" режимы выполнения операции вывода (для базовых типов) определены в заголовочном файле iostream.h, который необходимо поместить в начале текста программы до текста операции-функции operator << ().

    В качестве еще одного примера перегрузки (расширения действия) операции вывода <<, рассмотрим следующую программу, в которой для представления почтового адреса используется структурный тип с названием address:

//OOР8_1.СРР -  перегрузка  операции вывода  "<<".
#include <iostream.h>
struct address	//  Почтовый адрес.
{ 
   char *country;       // Страна.
   char *city;          // Город.
   char *street;        // Улица.
   int number_of_house; // Номер дома.
};
// Определение  операции-функции, "распространяющей" 
// действие операции включения в поток << на операнд 
// типа address: 
ostream&  // Тип возвращаемого значения.
    operator << (ostream& out,   address  ad) 
{
  out << "\nCountry: " << ad.country;
  out << "\nCity:    " << ad.city;
  out << "\nStreet:  " << ad.street;
  out << "\nHouse:   " << ad.number_of_house;
return out;
}
void main()
{ 
  address  ad = { "Russia", "Kurgan", "Gorky", 27 };
  cout <<"\nЗначение структуры (почтовый адрес):";
  cout << ad << "\n";
}
Текст этой программы можно взять здесь.

    Результаты выполнения программы:

    Значение структуры   (почтовый адрес): 
    Country:   Russia 
    City:      Kurgan
    Street:    Gorky
    House:     27

    В теле операции-функции operator << () использовано несколько операторов с выражениями, выводящими значения в поток вывода. В операторе return возвращается ссылка на него (имеющая тип ostream&).

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




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