Д. Стефенс - C++. Сборник рецептов
- Название:C++. Сборник рецептов
- Автор:
- Жанр:
- Издательство:КУДИЦ-ПРЕСС
- Год:2007
- Город:Москва
- ISBN:5-91136-030-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Д. Стефенс - C++. Сборник рецептов краткое содержание
Данная книга написана экспертами по C++ и содержит готовые рецепты решения каждодневных задач для программистов на С++. Один из авторов является создателем библиотеки Boost Iostreams и нескольких других библиотек C++ с открытым исходным кодом. В книге затрагивается множество тем, вот лишь некоторые из них: работа с датой и временем; потоковый ввод/вывод; обработка исключений; работа с классами и объектами; сборка приложений; синтаксический анализ XML-документов; программирование математических задач. Читатель сможет использовать готовые решения, а сэкономленное время и усилия направить на решение конкретных задач.
C++. Сборник рецептов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Чтобы добиться нужного эффекта, перегрузите 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;
Вторая строка — это краткая запись вызова Balance::operator=(y)
. Оператор присвоения отличается от большинства других операторов тем, что если вы не создаете собственной его версии, то компилятором создается версия по умолчанию. Версия по умолчанию просто копирует в текущий объект каждый член целевого объекта, что, конечно, не всегда приемлемо, так что его можно перегрузить и обеспечить другое поведение или перегрузить и предоставить возможность присвоения объектов типов, отличных от текущего
Для класса Balance
из примера 8.15 оператор присвоения можно определить вот так.
Balance& operator=(const Balance& other) {
val_ = other.val_;
return(*this);
}
Первое, на что вы должны обратить внимание, если не знакомы с перегрузкой операторов, — это синтаксис operator=
. Именно так объявляются все операторы. Все операторы можно рассматривать как функции с именами operator[symbol]
, где symbol
— это перегружаемый оператор. Единственным различием между операторами и обычными функциями является синтаксис их вызова. На самом деле, если вы хотите ввести побольше кода и написать отвратительно выглядящий код, то операторы можно вызывать и с помощью такого синтаксиса.
x.operator=(y); // То же самое, что и x = y, но уродливее
Работа моей реализации оператора присвоения проста. Он обновляет член val_
текущего объекта, записывая в него значение аргумента other
, а затем возвращает ссылку на текущий объект. Операторы присвоения возвращают текущий объект как ссылку, так что вызывающий код может использовать присвоение в выражениях:
Balance x, y, z;
// ...
x = (y = z);
Таким образом, возвращаемое из (y = z)
значение — это измененный объект y
, который затем передается в оператор присвоения объекта x
. Такая запись для присвоения используется не так часто, как для арифметических операторов, но чтобы придерживаться общего соглашения, всегда следует возвращать ссылку на текущий объект (то, как это связано с арифметическими операторами, я рассказываю дальше).
Интервал:
Закладка: