Создание перегруженных операторов вывода



 

В качестве простого примера рассмотрим создание оператора вывода для следующей версии класса three_d .

 

 

Чтобы создать операторную функцию вывода для объектов типа three_d , необходимо перегрузить оператор "<<" . Вот один из возможных способов.

 

 

Рассмотрим внимательно эту функцию, поскольку ее содержимое характерно для многих функций вывода данных. Во‑первых, отметьте, что согласно объявлению она возвращает ссылку на объект типа ostream . Это позволяет несколько операторов вывода объединить в одном составном выражении. Затем обратите внимание на то, что эта функция имеет два параметра. Первый представляет собой ссылку на поток, который используется в левой части оператора. Вторым является объект, который стоит в правой части этого оператора. (При необходимости второй параметр также может иметь тип ссылки на объект.) Само тело функции состоит из инструкций вывода трех значений координат, содержащихся в объекте типа three_d , и инструкции возврата потока stream .

Перед вами короткая программа, в которой демонстрируется использование оператора вывода.

 

 

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

 

 

Если удалить код, относящийся конкретно к классу three_d , останется "скелет" , подходящий для любой функции вывода данных.

 

 

Как уже отмечалось, для параметра obj разрешается использовать передачу по ссылке. В широком смысле конкретные действия функции вывода определяются программистом. Но если вы хотите следовать профессиональному стилю программирования, то ваша функция вывода должна все‑таки выводить информацию. И потом, всегда нелишне убедиться в том, что она возвращает параметр stream .

Прежде чем переходить к следующему разделу, подумайте, почему функция вывода для класса three_d не была закодирована таким образом.

 

 

В этой версии функции жестко закодирован поток cout . Это ограничивает круг ситуаций, в которых ее можно использовать. Помните, что оператор "<<" можно применить к любому потоку и что поток, который использован в "<<" ‑выражении, передается параметру stream . Следовательно, вы должны передавать функции поток, который корректно работает во всех случаях. Только так можно создать функцию вывода данных, которая подойдет для использования в любых выражениях ввода‑вывода.

 

Использование функций‑"друзей" для перегрузки операторов вывода

 

В предыдущей программе перегруженная функция вывода не была определена как член класса three_d . В действительности ни функция вывода, ни функция ввода не могут быть членами класса. Дело здесь вот в чем. Если операторная функция является членом класса, левый операнд (неявно передаваемый с помощью указателя this ) должен быть объектом класса, который сгенерировал обращение к этой операторной функции. И это изменить нельзя. Однако при перегрузке операторов вывода левый операнд должен быть потоком, а правый – объектом класса, данные которого подлежат выводу. Следовательно, перегруженные операторы вывода не могут быть функциями‑членами.

В связи с тем, что операторные функции вывода не должны быть членами класса, для которого они определяются, возникает серьезный вопрос: как перегруженный оператор вывода может получить доступ к закрытым элементам класса? В предыдущей программе переменные х , у z были определены как открытые, и поэтому оператор вывода без проблем мог получить к ним доступ. Но ведь сокрытие данных – важная часть объектно‑ориентированного программирования, и требовать, чтобы все данные были открытыми, попросту нелогично. Однако существует решение и для этой проблемы: оператор вывода можно сделать "другом" класса. Если функция является "другом" некоторого класса, то она получает легальный доступ к его private ‑данным. Как можно объявить "другом" класса перегруженную функцию вывода, покажем на примере класса three_d .

 

 

Обратите внимание на то, что переменные х , у и z в этой версии программы являются закрытыми в классе three_d , тем не менее, операторная функция вывода обращается к ним напрямую. Вот где проявляется великая сила "дружбы" : объявляя операторные функции ввода и вывода "друзьями" класса, для которого они определяются, мы тем самым поддерживаем принцип инкапсуляции объектно‑ориентированного программирования.

 

Перегрузка операторов ввода

 

Для перегрузки операторов ввода используйте тот же метод, который мы применяли при перегрузке оператора вывода. Например, следующий оператор ввода обеспечивает ввод трехмерных координат. Обратите внимание на то, что он также выводит соответствующее сообщение для пользователя.

 

 

Оператор ввода должен возвращать ссылку на объект типа istream . Кроме того, первый параметр должен представлять собой ссылку на объект типа istream . Этот тип принадлежит потоку, указанному слева от оператора ">>" . Второй параметр является ссылкой на переменную, которая принимает вводимое значение. Поскольку второй параметр – ссылка, он может быть модифицирован при вводе информации.

Общий формат оператора ввода имеет следующий вид.

 

 

Использование функции ввода данных для объектов типа three_d демонстрируется в следующей программе.

 

 

Вот как выглядит один из возможных результатов выполнения этой программы.

 

 

Подобно функциям вывода, функции ввода не должны быть членами класса, для обработки данных которого они предназначены. Они могут быть "друзьями" этого класса или просто независимыми функциями.

За исключением того, что функция ввода должна возвращать ссылку на объект типа istream , тело этой функции может содержать все, что вы считаете нужным в нее включить. Но логичнее использовать операторы ввода все же по прямому назначению, т.е. для выполнения операций ввода.

 

Сравнение С‑ и С++‑систем ввода‑вывода

 

Как вы знаете, предшественник C++, язык С, оснащен одной из самых гибких (среди структурированных языков) и при этом очень мощных систем ввода‑вывода. (Не будет преувеличением сказать, что среди всех известных структурированных языков С‑система ввода‑вывода не имеет себе равных.) Почему же тогда, спрашивается, в C++ определяется собственная система ввода‑вывода, если в ней продублирована большая часть того, что содержится в С (имеется в виду мощный набор С‑функций ввода‑вывода)? Ответить на этот вопрос нетрудно. Дело в том, что С‑система ввода‑вывода не обеспечивает никакой поддержки для объектов, определяемых пользователем. Например, если создать в С такую структуру

 

 

то существующую в С систему ввода‑вывода невозможно настроить так, чтобы она могла выполнять операции ввода‑вывода непосредственно над объектами типа my_struct . Но поскольку центром объектно‑ориентированного программирования являются именно объекты, имеет смысл, чтобы в C++ функционировала такая система ввода‑вывода, которую можно было бы динамически "обучать" обращению с любыми объектами, создаваемыми программистом. Именно поэтому для C++ и была изобретена новая объектно‑ориентированная система ввода‑вывода. Как вы уже могли убедиться, С++‑подход к вводу‑выводу позволяет перегружать операторы "<<" и ">>" , чтобы они могли работать с классами, создаваемыми программистами.

И еще. Поскольку C++ является супермножеством языка С, все содержимое С‑системы ввода‑вывода включено в C++. (См. приложение А, в котором представлен обзор С‑ориентированных функций ввода‑вывода.) Поэтому при переводе С‑программ на язык C++ вам не нужно изменять все инструкции ввода‑вывода подряд. Работающие С‑инструкции скомпилируются и будут успешно работать и в новой С++‑среде. Просто вы должны учесть, что старая С‑система ввода‑вывода не обладает объектно‑ориентированными возможностями.

 

Форматированный ввод‑вывод данных

 

До сих пор при вводе или выводе информации в наших примерах программ действовали параметры форматирования, которые по умолчанию использует С++‑система ввода‑вывода. Но программист может сам управлять форматом представления данных, причем двумя способами. Первый способ предполагает использование функций‑членов класса ios , а второй– функций специального типа, именуемых манипуляторами (manipulator). Мы же начнем освоение возможностей форматирования с функций‑членов класса ios .

 

Форматирование данных с использованием функций‑членов класса ios

 

В системе ввода‑вывода C++ каждый поток связан с набором флагов форматирования, которые управляют процессом форматирования информации. В классе ios объявляется перечисление fmtflags , в котором определены следующие значения. (Точнее, эти значения определены в классе ios_base , который, как упоминалось выше, является базовым для класса ios .)

 

 

Эти значения используются для установки или очистки флагов форматирования с помощью таких функций, как setf() и unsetf() . При использовании старого компилятора может оказаться, что он не определяет тип перечисления fmtflags . В этом случае флаги форматирования будут кодироваться как целочисленные long ‑значения.

 

Если флаг skipws установлен, то при потоковом вводе данных ведущие "пробельные" символы, или символы пропуска (т.е. пробелы, символы табуляции и новой строки), отбрасываются. Если же флаг skipws сброшен, пробельные символы не отбрасываются.

 

Если установлен флаг left , выводимые данные выравниваются по левому краю, а если установлен флаг right – по правому. Если установлен флаг internal , числовое значение дополняется пробелами, которыми заполняется поле между ним и знаком числа или символом основания системы счисления. Если ни один из этих флагов не установлен, результат выравнивается по правому краю по умолчанию.

 

По умолчанию числовые значения выводятся в десятичной системе счисления. Однако основание системы счисления можно изменить. Установка флага oct приведет к выводу результата в восьмеричном представлении, а установка флага hex – в шестнадцатеричном. Чтобы при отображении результата вернуться к десятичной системе счисления, достаточно установить флаг dec .

 

Установка флага showbase приводит к отображению обозначения основания системы счисления, в которой представляются числовые значения. Например, если используется шестнадцатеричное представление, то значение 1F будет отображено как 0x1F .

 

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

 

Установка флага showpos вызывает отображение ведущего знака "плюс" перед положительными значениями.

 

Установка флага showpoint приводит к отображению десятичной точки и хвостовых нулей для всех чисел с плавающей точкой – нужны они или нет.

 

После установки флага scientific числовые значения с плавающей точкой отображаются в экспоненциальном представлении. Если установлен флаг fixed , вещественные значения отображаются в обычном представлении. Если не установлен ни один из этих флагов, компилятор сам выбирает соответствующий метод представления.

 

При установленном флаге unitbuf содержимое буфера сбрасывается на диск после каждой операции вывода данных.

 

Если установлен флаг boolalpha , значения булева типа можно вводить или выводить, используя ключевые слова true и false .

 

Поскольку часто приходится обращаться к полям oct , dec и hex , на них допускается коллективная ссылка ios::basefield . Аналогично поля left , right и internal можно собирательно назвать ios::adjustfield . Наконец, поля scientific и fixed можно назвать ios::floatfield .

Чтобы установить флаги форматирования, обратитесь к функции setf().

Для установки любого флага используется функция setf() , которая является членом класса ios . Вот как выглядит ее формат.

 

 

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

 

 

Здесь элемент stream означает поток, параметры форматирования которого вы хотите изменить. Обратите внимание на использование префикса ios:: для уточнения принадлежности параметра showbase . Поскольку параметр showbase представляет собой перечислимую константу, определенную в классе ios , то при обращении к ней необходимо указывать имя класса ios . Этот принцип относится ко всем флагам форматирования. В следующей программе функция setf() используется для установки флагов showpos и scientific .

 

 

Вот как выглядят результаты выполнения этой программы.

 

 

С помощью операции ИЛИ можно установить сразу несколько нужных флагов форматирования в одном вызове функции setf() . Например, предыдущую программу можно сократить, объединив по ИЛИ флаги scientific и showpos , поскольку в этом случае выполняется только одно обращение к функции setf() .

 

 

Чтобы сбросить флаг, используйте функцию unsetf() , прототип которой выглядит так.

 

 

Для очистки флагов форматирования используется функция unsetf().

В этом случае будут обнулены флаги, заданные параметром flags . (При этом все другие флаги остаются в прежнем состоянии.)

Чтобы получить текущие установки флагов форматирования, используйте функцию flags().

Для того чтобы узнать текущие установки флагов форматирования, воспользуйтесь функцией flags() , прототип которой имеет следующий вид.

 

 

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

При использовании следующего формата вызова функции flags() устанавливаются значения флагов форматирования в соответствии с содержимым параметра flags и возвращаются их предыдущие значения.

 

 

Чтобы понять, как работают функции flags() и unsetf() , рассмотрим следующую программу. Она включает функцию showflags() , которая отображает состояние флагов форматирования.

 

 

При выполнении эта программа отображает такие результаты. (Между этими и вашими результатами возможно расхождение, вызванное использованием различных компиляторов.)

 

 

В предыдущей программе обратите внимание на то, что тип fmtflags указан с префиксом ios :: . Дело в том, что тип fmtflags определен в классе ios . В общем случае при использовании имени типа или перечислимой константы, определенной в некотором классе, необходимо указывать соответствующее имя вместе с именем класса.

 


Дата добавления: 2018-09-22; просмотров: 373; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!