Перегрузка инкремента и декремента.
В прошлом уроке мы с вами разбирали перегрузку операторов и рассматривали
пример класса, реализующего работу со строкой. В этом классе была использованна
перегрузка бинарных операторов "=" и "+". Однако, нам с вами было бы неплохо
рассмотреть перегрузку и унарных операторов. В частности, инкремента и
декремента. Кроме свойства унарности, каждый из этих операторов обладает двумя
формами, а это при их перегрузке имеет большое значение.
Следует отметить, что в начальных версиях языка C++ при перегрузке операций
++ и -- не делалось различия между постфиксной и префиксной формами. Например:
#include <iostream>
using namespace std;
class Digit{
// Целое число.
int N;
public:
// Конструктор c параметром
Digit(int n)
{
N = n;
}
// функция для показа числа на экран
void display()
{
cout << "\nDigit: N = " << N << "\n";
}
// Компонентная функция (форма не различается):
Digit& operator -- ()
{
// Уменьшаем содержимое объекта
// в десять раз и возвращаем его
// на место вызова оператора
N /= 10;
return *this;
}
};
void main()
{
// создаем объект Z именно с ним
// мы и будем экспериментировать
Digit Z(100);
// показ объекта в первозданном виде
cout<<"\nObject Z (before):\n";
Z.display();
cout<<"\n-----------------\n";
// присваиваем объекту Pref выражение
// с префиксной формой (в данном случае
// сначала изменится Z, а затем произойдет
// присваивание).
Digit Pref=--Z;
// показываем результат работы
// префиксной формы
cout<<"\nPrefix\n";
cout<<"\nObject Pref:\n";
Pref.display();
cout<<"\nObject Z (after):\n";
Z.display();
cout<<"\n-----------------\n";
// присваиваем объекту Postf выражение
// с постфиксной формой (в данном случае
// (к сожалению) снова сначала
// изменится Z, а затем произойдет
// присваивание).
Digit Postf=Z--;
// показываем результат работы
// постфиксной формы
cout<<"\nPostfix\n";
cout<<"\nObject Postf:\n";
Postf.display();
cout<<"\nObject Z (after):\n";
Z.display();
}
______________________________________________________________
Результат работы программы:
Object Z (before):
Digit: N = 100
-----------------
Prefix
Object Pref:
Digit: N = 10
Object Z (after):
Digit: N = 10
-----------------
Postfix
Object Postf:
Digit: N = 1
Object Z (after):
Digit: N = 1
|
В современной же версии языка C++ принято следующее соглашение:
Перегрузка префиксных операций ++ и -- ничем не отличается от перегрузки
других унарных операций. Другими словами, функции конкретного класса: operator++
и operator--, определяют префиксные операции для этого класса.
При определении постфиксных операций "++" и "--" операции-функции должны
иметь еще один дополнительный параметр типа int. Когда в программе будет
использовано постфиксное выражение, то вызывается версия функции с параметром
типа int. При этом параметр передавать !не нужно!, а значение его в
функции будет равно нулю.
#include <iostream>
using namespace std;
// Класс, реализующий работу с "парой чисел"
class Pair{
// Целое число.
int N;
// Вещественное число.
double x;
public:
// Конструктор c параметрами
Pair(int n, double xn)
{
N = n;
x = xn;
}
// функция для показа данных на экран
void display()
{
cout << "\nPair: N = " << N << " x = " << x << "\n";
}
// Компонентная функция (префиксная --):
Pair& operator -- ()
{
// Уменьшаем содержимое объекта
// в десять раз и возвращаем его
// на место вызова оператора
N /= 10;
x /= 10;
return *this;
}
// Компонентная функция (постфиксная --):
Pair& operator -- (int k)
{
// временно сохраняем содержимое
// объекта в независимую
// переменную типа Pair
// (Попытка использовать здесь
// значение дополнительного параметра
// int k подтверждает его равенство 0.)
Pair temp(0,0.0);
temp.N=N+k;
temp.x=x+k;
// уменьшаем объект в 10 раз
N /= 10;
x /= 10;
// возвращаем прежнее значение объекта.
// таким "тактическим ходом"
// мы добиваемся эффекта постфиксной
// формы, т. е. в ситуации А=B++
// в А записывается текущее
// значение объекта B, тогда как сам B
// изменяется
return temp;
}
};
void main()
{
// создаем объект Z именно с ним
// мы и будем экспериментировать
Pair Z(10,20.2);
// показ объекта в первозданном виде
cout<<"\nObject Z (before):\n";
Z.display();
cout<<"\n-----------------\n";
// присваиваем объекту Pref выражение
// с префиксной формой (в данном случае
// сначала изменится Z, а затем произойдет
// присваивание).
Pair Pref=--Z;
// показываем результат работы
// префиксной формы
cout<<"\nPrefix\n";
cout<<"\nObject Pref:\n";
Pref.display();
cout<<"\nObject Z (after):\n";
Z.display();
cout<<"\n-----------------\n";
// присваиваем объекту Postf выражение
// с постфиксной формой (в данном случае
// сначала произойдет присваивание,
// а затем изменится Z).
Pair Postf=Z--;
// показываем результат работы
// постфиксной формы
cout<<"\nPostfix\n";
cout<<"\nObject Postf:\n";
Postf.display();
cout<<"\nObject Z (after):\n";
Z.display();
}
______________________________________________________________
Результат работы программы:
Object Z (before):
Pair: N = 10 x = 20.2
-----------------
Prefix
Object Pref:
Pair: N = 1 x = 2.02
Object Z (after):
Pair: N = 1 x = 2.02
-----------------
Postfix
Object Postf:
Pair: N = 1 x = 2.02
Object Z (after):
Pair: N = 0 x = 0.202
|
Примечание: В двух, вышеописанных примерах, мы
не используем конструктор копирования, несмотря на то, что здесь присутствует
инициализация одного объекта другим при создании. Это связано с тем, что в этом
нет необходимости, так как здесь побитовое копирование не несет критического
характера. Так что нет смысла перегружать код лишней конструкцией.