С++ Перегрузка операции << Или как вывести объект самописного класса на экран с помощью cout

Иногда хочется и иногда нужно перегрузить операции ввода или вывода таким образом, чтобы объект нашего самописного класса умел выводиться на экран или записывать в свои внутренние элементы. Например, вывод по подобию:


Когда мы пишем свои классы, классы надо многому учить. Голые классы почти ничего не умеют, в том числе и не знают операций <<, >>. Здесь описывается только перегрузка операции << и объясняется, почему оно так. С операцией >> дела обстоят также, только отличается тип.

Проблема в том, что в операции участвуют два объекта, типы которых разные: объект cout, тип которого ostream и наш собственный объект temp, тип которого MyInt. Когда мы перегружаем операцию, надо смотреть на первый объект, используемый в операции, его тип своего рода доминирующий. Другими словами, когда мы пишем cout <<, доминирует у нас тип от cout. Это обозначает, что чтобы написать:



нужно изменять класс, описывающий cout, т. е. класс ostream. Этот класс написан разработчиками Вашего компилятора и изменять его идея глупая. Не нужно трогать то, что не нужно трогать. Всё, что написано разработчиками, не трогаем до тех пор, пока сами не станем разработчиками компиляторов С++. Да и разобраться в файле, где описан класс ostream, не очень-то просто новичкам. В общем, опасно изменять класс ostream.
Из ситуации можно бы было выйти, перегрузив операцию <<, взяв доминирующим типом тип объекта temp, т. е. тип MyInt.

Операция хоть и заработала, но как-то зачем-то наоборот. Это не просто неудобно, оно ещё и запутать может, поэтому это весьма нехороший вариант решения. Мы разобрали, что чтобы привычный вариант работал, можно править класс, который мы править не должны, потому что правка его опасная затея. Вот тут у нас серьёзное ограничение выходит, но существуют дружественные функции, которые помогут нам и класс, написанный разработчиками, не сломать и цели нашей достигнуть: привести операцию вывода на экран, применяемую к объекту нашего класса, в вид надлежащий, т. е. благодаря использованию дружественных функций мы таки сможем написать cout << temp

Если Вы ненароком забудете использовать метод для вытаскивания значения, то произойдёт бесконечная рекурсия и программа сломается. У Вас получится (ostream << MyInt) будет провоцировать выполнение (ostream << MyInt), провоцирующую выполнение (ostream << MyInt) и так без конца.
  • Когда в перегружающую операцию функцию, мы отдаём в первый параметр значение, значение обязательно должно быть принято параметром по ссылке. Это связано с тем, что потоки копироватся не умеют: параметр функции не сможет перенсти в себя байты входящего значения, локальная копия не может быть создана из-за этого. Остаётся только работа напрямую с объектом, для этого используется ссылка на объект. Кроме вывода на экран возможны и другие выводы: вывод в файл, вывод на принтер и др. Из-за этого лучше использовать в функции, перегружающей операцию << не cout, а параметр, который оказывается ссылкой на объект входящего потока. Хотя можно написать:



    такой вариант ограничен потоком вывода на экран, т. е. намного менее универсален. Поэтому предпочтение отдаётся объекту на какой-либо вывод вообще.
Несмотря на то, что выглядит так, как будто задача решена, не стоит торопиться. Есть одна ещё неразрешённость. Нельзя таким способом, к которому мы пришли, создавать цепочки вывода:


Связано это с возвращаемым из перегружающей операцию << функции-члена класса типом. У нас возвращается void.
Чтобы понять, почему не работает цепочка, нужно понять, как происходит движение операций. Предположим, что есть следующие выражения:



C++ читает выражение вывода (3-я строчка) слева-направо, подразумевая следующий эквивалент (cout << х) << у;

Выражение в скобках работает, но потом, согласно нашей перегрузке, результатом выполнения операции отдаётся тип void, но тип void не класс, для него не описано операции <<, при применении операции << к результату, полученному в скобках, провоцируется ошибка. Если кратко, то после первого вычисления доминирует тип void, к которому операцию << применить нельзя.

Выход из сложившейся ситуации — изменить возвращаемый тип на тип, умеющий работать с <<. Поскольку нас интересуют потоки вывода (в данном случае вывод на экран, но у кого-то может быть в другое устройство или в файл), то меняем возвращаемый тип на тип, обозначающий потоки вывода, т. е. void меняем на ostream&. Возвращать надо тоже ссылку. По тем же причинам: потоки не умеют копироваться.

Использованные материалы:

Язык программирования C++. Лекции и упражнения. 6-е изд (Стивен Прата)

Статья была полностью переписана 15.04.2018

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/vandad-nahavandipur/ios-priemy-programmirovaniya/?lfrom=15589587

Последние комментарии

Яндекс.Метрика