На этом шаге мы начнем рассматривать перегрузку операций ввода-вывода.
При передачах данных базовых типов потоки 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&).
На следующем шаге мы закончим разбирать перегрузку операции вывода.