Сегодня мы познакомимся с замечательной возможностью нашего любомого языка C++ - перегрузкой операций. Но сначала разберёмся, для чего это нужно.

С базовыми типами вы можете использовать любые операции: +, -, *, ++, = и многие другие. Например:

int a=2,b=3,c;
c = a + b;

Здесь над переменными типа int сначала выполняется операция +, а затем результат присваивается переменной c с помощью операции =. Над классами такое проделать не получится. Создадим простой класс:

код на языке c++
class Counter
{
public:
  int counter;
  Counter() : c(0) {}
};

Counter a,b,c;
a.counter = 2;
b.counter = 3;
c = a + b;

Здесь компилятор выдаст ошибку на последней строке: он не знает как именно нужно действовать при использовании операций = и + над объектами класса Counter. Можно решить данную проблему вот так:

код на языке c++
class Counter
{
public:
  int counter;
  Counter() : count(0) {}
  void AddCounters (Counter& a, Counter& b)
  {
    counter = a.counter + b.counter;
  }
};

Counter a,b,c;
a.counter = 2;
b.counter = 3;
c.AddCounters(a,b);

Согласитесь, использование операции + и = в данном случае сделало бы программу более понятной. Так вот, чтобы использовать стандартные операции C++ с классами, эти операции нужно перегрузить (overload).

Перегрузка унарных операций

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

код на языке c++
int a=1;
++a; // a = 2;
--a; // a = 1;

Теперь научим класс Counter использовать преинкремент (предекремент):

код на языке c++
class Counter
{
private:
  counter;

public:
  Counter() : counter(0) {}
  void operator++ ()
  {
    counter += 1; // Можно также counter++ или ++counter -
                  // это  не имеет значения  в данном случае.
  }
};

Counter a;
++a;
++a;
++a;

Этот код работает. При использовании операции ++ (важно чтобы этот знак находился до идентификатора!) над объектом класса Counter, происходит увеличение переменной counter объекта a.

В данном примере мы перегрузили операцию ++. Делается это созданием метода внутри класса. Единственной важной особенностью данного метода - имя идентификатора. Имя идентификатора для перегруженных операций состоит из ключевого слова operator и названия операции. Во всём остальном этот метод определяется как и любые другие.

Использовать перегруженные операции с пользовательскими типами очень просто - так же как и с обычными типами данных.

Перегрузка постфиксных операций

Примеры постфиксной операции:

int a = 3;
a++;
a--;

То есть, здесь знак операции ставится после имени идентификатора. Для использования постфиксных операций с пользовательскими типами данных нужно совсем немного:

код на языке c++
public:
  void operator++ ()
  {
    counter += 1;
  }
  void operator++ (int)
  {
    counter += 1;
  }

Единственным отличием префиксной операции от постфиксной - ключевое слово int в списке аргументов. Но int - это не аргумент! Это слово говорит, что перегружается постфиксная операция. Теперь операцию ++ можно использовать как перед идентификатором объекта, так и после:

Counter a;
++a;
++a;
a++;
a++;

Перегрузка бинарных операций

Перегрузка операций с двумя аргументами очень похожа на перегрузку бинарных операций:

код на языке c++
Counter operator+ (Counter t)
{
  Counter summ;
  summ.counter = counter + t.counter;
  return summ;
}

Counter c1,c2,c3;
c1.counter = 3;
c2.counter = 2;

c3 = c1 + c2;

Какая переменная вызовет функцию operator+? В перегруженных бинарных операциях всегда вызывается метод левого операнда. В данном случае метод operator+ вызывает объект c1.

В метод мы передаём аргумент. Аргументом всегда является правый операнд.

Кроме того, в данном случае операция + должна вернуть какой-то результат, чтобы присвоить его объекту c3. Мы возвращаем объект Counter. Возвращаемое значение присваивается переменной c3.

Заметьте, мы перегрузили операцию +, но не перегружали операцию =! Конечно же нужно добавить этот метод к классу Counter:

код на языке c++
Counter operator= (Counter t)
{
  Counter assign;
  counter = t.counter;
  assign.counter = t.counter;
  return assign;
}

Внутри метода мы создали дополнительную переменную assign. Данная переменная используется, чтобы можно было работать вот с таким кодом:

Counter c1(5),c2,c3;
c3 = c2 = c1;

Хотя, для возвращаемого значения можно воспользоваться и более элегантными способами, с которыми мы вскоре познакомимся.