C++. Сборник рецептов - [122]

Шрифт
Интервал

Представьте себе инкремент целого числа. С помощью оператора >++ имеется два способа выполнить его для некоторого целого >i.

>i++; // постфиксный

>++i; // префиксный

Оба инкрементируют >i: первая версия создает временную копию >i, инкрементирует >i и затем возвращает временное значение, а вторая инкрементирует >i и затем возвращает его. C++ позволяет выполнять перегрузку операторов, что означает, что вы можете заставить свой собственный тип (класс или >enum) вести себя так же, как и >int.

Чтобы добиться нужного эффекта, перегрузите >operator++ и >operator--. Пример 8.14 иллюстрирует, как перегружать префиксную и постфиксную версии.

>Score& operator++() { // префиксный

> ++score_;

> return(*this);

>}


>const Score operator++(int) { // постфиксный

> Score tmp(*this);

> ++(*this);

> return(tmp);

>}

Префикс выглядит так, как и следует ожидать, но компилятор различает эти две версии, и в объявление постфиксной версии включается параметр >int. Он не имеет семантического применения — он всегда передается как ноль, так что его можно игнорировать.

После этого класс >Score можно использовать как >int.

>Score player1(50);

>player1++;

>++player1; // score_ = 52

Вы, вероятно, заметили, что сигнатуры префиксной версии >operator++ возвращают ссылку на текущий класс. Именно так и следует делать (а не возвращать, к примеру, >void), чтобы инкрементируемый или декрементируемый объект мог использоваться в других выражениях. Рассмотрим такую строку из примера.

>(--player1)--;

Да, это странно, но она иллюстрирует этот момент. Если бы префиксный >operator-- не возвращал чего-то осмысленного, то это выражение не скомпилировалось бы. Еще один пример показывает вызов функции.

>foo(--player1);

Функция >foo ожидает аргумент типа >Score, и для корректной компиляции именно это должно возвращаться из префиксного >operator--.

Перегрузка операторов — это мощная возможность, которая позволяет для типов, определяемых пользователем, использовать те же операторы, что и для встроенных типов. Сторонники других языков, которые не поддерживают перегрузку операторов, утверждают, что эта возможность сбивает с толку и очень сложна, и, следует признать, может быть перегружено очень много операторов, соответствующих любому поведению. Но когда дело касается простого инкремента и декремента, хорошо иметь возможность изменить поведение класса так, как этого хочется.

Смотри также

Рецепт 8.14.

8.14. Перегрузка арифметических операторов и операторов присвоения для работы с классами

Проблема

Имеется класс, для которого имеют смысл некоторые из унарных или бинарных операторов С++, и требуется, чтобы пользователи класса могли использовать их при работе с объектами этого класса. Например, если есть класс с именем >Balance, который содержит значение с плавающей точкой (например, баланс счета), будет удобно, если для объектов >Balance можно было бы использовать некоторые стандартные операторы С++, как здесь.

>Balance checking(50.0);

>savings(100.0);

>checking += 12.0;

>Balance total = checking + savings;

Решение

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

Пример 8.15. Перегрузка унарных и бинарных операторов

>#include


>using namespace std;


>class Balance {

> // These have to see private data

> friend const Balance operator+(const Balance& lhs, const Balance& rhs);

> friend const Balance operator+(double lhs, const Balance& rhs);

> friend const Balance operator+(const Balance& lhs, double rhs);

>public:

> Balance() : val_(0.0) {}

> Balance(double val) : val_(val) {}

> ~Balance() {}


> // Унарные операторы

> Balance& operator+=(const Balance& other) {

>  val_ += other.val_;

>  return(*this);

> }

> Balance& operator+=(double other) {

>  val_ += other;

>  return(*this);

> }

> double getVal() const {return(val_);}

>private:

> double val_;

>};


>// Бинарные операторы

>const Balance operator+(const Balance& lhs, const Balance& rhs) {

> Balance tmp(lhs.val_ + rhs.val_);

> return(tmp);

>}


>const Balance operator+(double lhs, const Balance& rhs) {

> Balance tmp(lhs + rhs.val_);

> return(tmp);

>}


>const Balance operator+(const Balance& lhs, double rhs) {

> Balance tmp(lhs.val_ + rhs);

> return(tmp);

>}


>int main() {

> Balance checking(500.00);

> savings(23.91);

> checking += 50;

> Balance total = checking + savings;

> cout << "Платежный баланс: " << checking.getVal() << '\n';

> cout << "Общий баланс: " << total.getVal() << '\n';

>}

Обсуждение

Наиболее часто используют перегрузку для арифметических операторов и операторов присвоения. Существует огромное количество различных классов, для которых имеют смысл арифметические операторы (сложение, умножение, остаток от деления, сдвиг битов вправо/влево) и операторы присвоения — вне зависимости от того, используются ли они для вычислений или для чего-то другого. Пример 8.15 показывает основные методики перегрузки этих операторов.

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

>Balance x(0), у(32);

>x = y;


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


Создаем порт для FreeBSD своими руками. Часть II

Система сборки программ, используемая во FreeBSD, имеет значительно большие возможности, чем те, которые мы задействовали. Какие это возможности и как их использовать в своих портах?


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.


Как пасти котов. Наставление для программистов, руководящих другими программистами

«Как пасти котов» – это книга о лидерстве и руководстве, о том, как первое совмещать со вторым. Это, если хотите, словарь трудных случаев управления IT-проектами. Программист подобен кошке, которая гуляет сама по себе. Так уж исторически сложилось. Именно поэтому так непросто быть руководителем команды разработчиков. Даже если вы еще месяц назад были блестящим и дисциплинированным программистом и вдруг оказались в роли менеджера, вряд ли вы знаете, с чего надо начать, какой выбрать стиль руководства, как нанимать и увольнять сотрудников, проводить совещания, добиваться своевременного выполнения задач.