- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
Упражнение 7.23. Напишите собственную версию класса Screen.
Упражнение 7.24. Добавьте в свой класс Screen три конструктора: стандартный; получающий высоту, ширину и заполняющий содержимое соответствующим количеством пробелов; получающий высоту, ширину и заполняющий символ для содержимого экрана.
Упражнение 7.25. Может ли класс Screen безопасно полагаться на заданные по умолчанию версии операторов копирования и присвоения? Если да, то почему? Если нет, то почему?
Упражнение 7.26. Определите функцию Sales data::avg_price как встраиваемую.
7.3.2. Функции, возвращающие указатель *this
Теперь добавим функции, устанавливающие символ в курсоре или в заданной области:
class Screen {
public:
Screen &set(char);
Screen &set(pos, pos, char);
// другие члены, как прежде
};
inline Screen &Screen::set(char c) {
contents[cursor] = с; // установите новое значение в текущей позиции
// курсора
return *this; // возвратить этот объект как l-значение
}
inline Screen &Screen::set(pos r, pos col, char ch) {
contents[r * width + col] = ch; // установить позицию по данному
// значению
return *this; // возвратить этот объект как l-значение
}
Как и функция move(), функция-член set() возвращает ссылку на объект, из которого они вызваны (см. раздел 7.1.2). Возвращающие ссылку функции являются l-значениями (см. раздел 6.3.2), а это означает, что они возвращают сам объект, а не его копию. Это позволяет связать несколько их вызовов в одно выражение:
// переместить курсор в указанную позицию и присвоить
// символу значение
myScreen.move(4,0).set('#');
Эти операции выполнятся для того же объекта. В этом выражении сначала перемещается курсор (move()) в окно (myScreen), а затем устанавливается (set()) заданный символ. Таким образом, этот оператор эквивалентен следующему:
myScreen.move(4,0);
myScreen.set('#');
Если бы функции move() и set() возвращали тип Screen, а не Screen&, этот оператор выполнялся бы совсем по-другому. В данном случае он был бы эквивалентен следующему:
// если move возвращает Screen, а не Screen&
Screen temp = myScreen.move(4,0); // возвращаемое значение было
// бы скопировано
temp.set('#'); // содержимое myScreen осталось бы неизменно
Если бы функция move() имела возвращаемое значение не ссылочного типа, то оно было бы копией *this (см. раздел 6.3.2). Вызов функции set() изменил бы лишь временную копию, а не сам объект myScreen.
Возвращение *this из константной функции-членаТеперь добавим функцию display(), выводящую содержимое окна. Необходима возможность включать эту операцию в последовательность операций set() и move(). Поэтому, подобно функциям set() и move(), функция display() возвратит ссылку на объект, для которого она выполняется.
Логически отображение объекта класса Screen (окна) не изменяет его, поэтому функцию display() следует сделать константным членом. Но если функция display() будет константной, то this будет указателем на константу, а значение *this — константным объектом. Следовательно, типом возвращаемого значения функции display() будет const Screen&. Однако, если функция display() возвратит ссылку на константу, мы не сможем вставить вызов функции display() в последовательность действий:
Screen myScreen;
// если display возвращает константную ссылку,
// вызов в последовательности будет ошибкой
myScreen.display(cout).set('*');
Хотя объект myScreen неконстантный, вызов функции set() не будет компилироваться. Проблема в том, что константная версия функции display() возвращает ссылку на константу, и мы не можем вызвать функцию set() для константного объекта.
Тип возвращаемого значения константной функции-члена, возвращающей *this как ссылку, должен быть ссылкой на константу.
Перегрузка на основании константностиФункции-члены вполне можно перегружать исходя из того, являются ли они константными или нет, причем по тем же причинам, по которым функцию можно перегружать исходя из того, является ли ее параметр указателем на константу (см. раздел 6.4). Неконстантная версия неприменима для константных объектов; она применима только для константных объектов. Для неконстантного объекта можно вызвать любую версию, но неконстантная версия будет лучшим соответствием.
В этом примере определим закрытую функцию-член do_display() для фактического вывода окна. Каждая из функций display() вызовет эту функцию, а затем возвратит объект, для которого она выполняется:
class Screen {
public:
// display перегружена на основании того, является ли
// объект константой или нет
Screen &display(std::ostream &os)
{ do_display(os); return *this; }
const Screen &display(std::ostream &os) const
{ do_display(os); return *this; }
private:
// функция отображения окна
void do_display(std::ostream &os) const {os << contents;}
// другие члены как прежде
};
Как и в любом другом случае, при вызове одной функции-члена другой неявно передается указатель this. Таким образом, когда функция display() вызывает функцию-член do_display(), ей неявно передается собственный указатель this. Когда неконстантная версия функции display() вызывает функцию do_display(), ее указатель this неявно преобразуется из указателя на неконстанту в указатель на константу (см. раздел 4.11.2).
Когда функция do_display() завершает работу, функция display() возвращает объект, с которым они работают, обращаясь к значению указателя this. В неконстантной версии указатель this указывает на неконстантный объект, так что эта версия функции display() возвращает обычную, неконстантную ссылку; константная версия возвращает ссылку на константу.
Когда происходит вызов функции display() для объекта, вызываемую версию определяет его константность:
Screen myScreen(5, 3);
const Screen blank(5, 3);
myScreen.set('#').display(cout); // вызов неконстантной версии
blank.display(cout); // вызов константной версии
Совет. Используйте закрытые вспомогательные функцииНекоторые читатели могут удивиться: зачем дополнительно создавать отдельную функцию do_display()? В конце концов, обращение к функции do_display() не намного проще, чем осуществляемое в ней действие.
Зачем же она нужна? Причин здесь несколько.
• Всегда желательно избегать нескольких экземпляров одного кода.
• По мере развития класса функция display() может стать значительно более сложной, а следовательно, преимущества одной, а не нескольких копий кода станут более очевидными.
• Во время разработки в тело функции display(), вероятно, придется добавить отладочный код, который в финальной версии будет удален. Это будет проще сделать в случае, когда весь отладочный код находится в одной функции do_display().
• Поскольку функция do_display() объявлена встраиваемой (inline), при создании исполняемого кода компилятор и так вставит ее содержимое по месту вызова, поэтому вызов функции не повлечет за собой никаких потерь времени и ресурсов.
Обычно в хорошо спроектированных программах на языке С++ присутствует множество маленьких функций, таких как do_display(), которые выполняют всю основную работу, когда их использует набор других функций.
Упражнения раздела 7.3.2Упражнение 7.27. Добавьте функции move(), set() и display() в свою версию класса Screen. Проверьте свой класс, выполнив следующий код:
Screen myScreen(5, 5, 'X');
myScreen.move(4,0).set('#').display(cout);
cout << "n";
myScreen.display(cout);
cout << "n";
Упражнение 7.28. Что если бы в предыдущем упражнении типом возвращаемого значения функций move(), set() и display() был Screen, а не Screen&?
Упражнение 7.29. Пересмотрите свой класс Screen так, чтобы функции move(), set() и display() возвращали тип Screen, а затем проверьте свое предположение из предыдущего упражнения.

