20.4. Перегрузка оператора вывода
20.4. Перегрузка оператора вывода
Если мы хотим, чтобы наш тип класса поддерживал операции ввода/вывода, то необходимо перегрузить оба соответствующих оператора. В этом разделе мы рассмотрим, как перегружается оператор вывода. (Перегрузка оператора ввода – тема следующего раздела.) Например, для класса WordCount он выглядит так:
class WordCount {
friend ostream&
operator( ostream&, const WordCount& );
public:
WordCount( string word, int cnt=1 );
// ...
private:
string word;
int occurs;
};
ostream&
operator ( ostream& os, const WordCount& wd )
{ // формат: счетчик слово
os " " " " "
wd.word;
return os;
}
Проектировщик должен решить, следует ли выводить завершающий символ новой строки. Лучше этого не делать: поскольку операторы вывода для встроенных типов такой символ не печатают, пользователь ожидает аналогичного поведения и от операторов в других классах. Определенный нами в классе WordCount оператор вывода можно использовать вместе с любыми другими операторами:
#include iostream
#include " WordCount.h"
int main()
{
WordCount wd( " sadness" , 12 );
cout " wd: " wd endl;
return 0;
}
Программа печатает на терминале строки:
wd:
sadness
Оператор вывода – это бинарный оператор, который возвращает ссылку на объект класса ostream. В общем случае структура определения перегруженного оператора вывода выглядит так:
// структура перегруженного оператора вывода
ostream&
operator ( ostream& os, const ClassType &object )
{
// произвольный код для подготовки объекта
// фактическое число членов
os // ...
// возвращается объект ostream
return os;
}
Первый его аргумент – это ссылка на объект ostream, а второй – ссылка (обычно константная) на объект некоторого класса. Возвращается ссылка на ostream. Значением всегда является объект ostream, для которого оператор вызывался.
Поскольку первым аргументом является ссылка, оператор вывода должен быть определен как обычная функция, а не член класса. (Объяснение см. в разделе 15.1.) Если оператору необходим доступ к неоткрытым членам, то следует объявить его другом класса. (О друзьях говорилось в разделе 15.2.)
Пусть Location – это класс, в котором хранятся номера строки и колонки вхождения слова. Вот его определение:
#include iostream
class Location {
friend ostream& operator&&( ostream&, const Location& );
private:
short _line;
short _col;
};
ostream& operator &&( ostream& os, const Location& lc )
{
// объект Loc выводится в виде: 10,37
os " " lc._line
" ," lc._col " " ;
return os;
}
Изменим определение класса WordCount, включив в него вектор occurList объектов Location и объект word класса string:
#include vector
#include string
#include iostream
#include "Location.h"
class WordCount {
friend ostream& operator&&( ostream&, const WordCount& );
public:
WordCount() {}
WordCount( const string &word ) : _word( word ) {}
WordCount( const string &word, int ln, int col )
: _word( word ){ insert_location( ln, col ); }
string word() const { return _word; }
int occurs() const { return _occurList.size(); }
void found( int ln, int col )
{ insert_location( ln, col ); }
private:
void insert_location( int ln, int col )
{ _occurList.push_back( Location( ln, col )); }
string _word;
vector Location _occurList;
};
В классах string и Location определен оператор вывода operator(). Так выглядит измененное определение оператора вывода в WordCount:
ostream&
operator ( ostream& os, const WordCount& wd )
{
os "" wd._occurList.size() " "
wd._word endl;
int cnt = 0, onLine = 6;
vector Location ::const_iterator first =
wd._occurList.begin();
vector Location ::const_iterator last =
wd._occurList.end();
for ( ; first != last; ++first )
{
// os Location
os *first " ";
// форматирование: по 6 в строке
if ( ++cnt = onLine )
{ os " "; cnt = 0; }
}
return os;
}
А вот небольшая программа для тестирования нового определения класса WordCount; позиции вхождений для простоты “зашиты” в код:
int main()
{
WordCount search( "rosebud" );
// для простоты явно введем 8 вхождений
search.found(11,3); search.found(11,8);
search.found(14,2); search.found(34,6);
search.found(49,7); search.found(67,5);
search.found(81,2); search.found(82,3);
search.found(91,4); search.found(97,8);
cout "Вхождения: " " "
search endl;
return 0;
}
После компиляции и запуска программа выводит следующее:
Вхождения:
10 rosebud
11,3 11,8 14,2 34,6 49,7 67,5
81,2 82,3 91,4 97,8
Полученный результат сохранен в файле output. Далее мы определим оператор ввода, с помощью которого прочитаем данные из этого файла.
Упражнение 20.7
Дано определение класса Date:
class Date {
public:
// ...
private:
int month, day, year;
};
Напишите перегруженный оператор вывода даты в формате:
(a)
// полное название месяца
September 8th, 1997
(b)
9 / 8 / 97
(c) Какой формат лучше? Объясните.
(d) Должен ли оператор вывода Date быть функцией-другом? Почему?
Упражнение 20.8
Определите оператор вывода для следующего класса CheckoutRecord:
class CheckoutRecord { // запись о выдаче
public:
// ...
private:
double book_id; // идентификатор книги
string title; // название
Date date_borrowed; // дата выдачи
Date date_due; // дата возврата
pairstring,string borrower; // кому выдана
vector pairstring,string wait_list; // очередь на книгу
};
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Перегрузка операторов
Перегрузка операторов С++ позволяет нам перегружать функции, т.е. мы можем объявлять несколько функций с одним именем в одной и той же области видимости, если они имеют различные списки параметров. Кроме того, С++ поддерживает перегрузку операторов, позволяя назначать
R.13 Перегрузка
R.13 Перегрузка Говорят, что имя перегружено, если для него задано несколько различных описаний функций в одной области видимости. При использовании имени выбор правильной функции производится путем сопоставления типов формальных параметров с типами фактических
Пример оператора Select Case
Пример оператора Select Case Здесь явно не помешает пример, показывающий, как в действительности может выглядеть структура Select Case. Select Case objRol l OfFi l m.Type Case "Слайдовая" intСлайдовые = intСлайдовые + 1 Case "Цветная негативная" intЦветныеНегативные =
Назначение другого следующего оператора
Назначение другого следующего оператора Представьте, что вы, двигаясь сквозь свою программу в пошаговом режиме, вдруг обнаруживаете, что в следующем операторе содержится большая ошибка. Вместо того чтобы выполнить этот оператор и тем самым ввести свою программу в
5.2.5. Синтаксис составного оператора BEGIN … END
5.2.5. Синтаксис составного оператора BEGIN … END [begin_label:]BEGIN[statement_list]END [end_label]Синтаксис BEGIN … END используется для записи составных инструкций, которые могут появляться внутри сохраненных подпрограмм и триггеров. Составная инструкция может содержать много инструкций, взятых в
РАСШИРЕНИЕ ОПЕРАТОРА if С ПОМОЩЬЮ else
РАСШИРЕНИЕ ОПЕРАТОРА if С ПОМОЩЬЮ else Простейшей формой оператора if является та, которой мы только что воспользовались: if(выражение)оператор Обычно под выражением здесь понимают условное выражение, с его помощью сравниваются значения двух величин (например x > y
18.8.2. Прекращение выполнения оператора case
18.8.2. Прекращение выполнения оператора case Рассмотрим следующий пример. В сценарии выполняется бесконечный цикл до тех пор, пока пользователь не введет число, большее 5. Для прерывания цикла и возврата в командную строку интерпретатора используется команда break.$ pg
Синтаксическая перегрузка
Синтаксическая перегрузка Перегрузка - это связывание с одним именем более одного содержания. Наиболее часто перегружаются имена переменных: почти во всех языках программирования различные по смыслу переменные могут иметь одно и то же имя, если они принадлежат
Отправка с сайта оператора
Отправка с сайта оператора На сегодняшний день уже, наверное, все операторы поддерживают отправку SMS со своего сайта. Сообщения, правда, можно отправить только абонентам данного оператора, но этого, как правило, бывает достаточно.Итак, к практике. Сейчас мы рассмотрим