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




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

 

Механизм перегрузки операций позволяет реализовать распространение действия стандартных операций языка на переменные (операнды), которые являются объектами абстрактных (пользовательских) типов данных (классов и структур). Это дает возможность применять к объектам классов и структур арифметические операции, операции сравнения и пр. как к обычным переменным стандартных типов данных языка.

Не могут быть перегружены следующие операции:

Операция Описание
?: Условная операция
. Операция точка (операция доступа к элементу)
sizeof Операция определения размера
:: Операция области видимости
.* ->* Операции разрешения контекста

 

При перегрузки может быть изменен смысл самой операции. То есть допустимо сделать так, что запись вида x+y физически будет выполнять вычитание или деление (x и y – объекты класса). Естественно, не рекомендуется делать это на практике, это усложнит чтение и понимание кода программы.

По числу операндов все операции (за исключением тернарной операции:?) могут быть разделены на унарные (с одним операндом) и бинарные (с двумя операндами).

 

При перегрузке операций НЕВОЗМОЖНО изменить приоритет операций и её синтаксис (число операндов – сделать из унарной операции бинарную и наоборот). Также ЗАПРЕЩЕНО создавать и перегружать собственные операции (+?, ** и пр.). При перегрузке операций по крайней мере один из операндов должен иметь абстрактный тип – перегрузка операций для стандартных типов данных (int, float и пр.) НЕДОПУСТИМА.

 

Реализация перегрузки операций производится путем создания специальной, так называемой операторной функции с именем operator@, где @ - символ перегружаемого оператора.

<код возврата> operator@ (<аргументы>)

Для обеспечения связи операторной функции и класса, эта функция должна или являться членом класса, или быть внешней (для эффективности работы программы – дружественной) функцией с аргументами типа «объект класса» или «ссылка на объект класса».

Запись вида

x@y

интерпретируется компилятором как вызов функции

operator@(x,y) – если перегрузка бинарной операции @ реализована в виде дружественной функции (у функции два аргумента);

x.operator@(y) – если перегрузка бинарной операции @ реализована в виде компонентной функции класса (у функции один аргумент).

 

Вызов бинарной операции всегда реализует операнд, стоящий слева от операции. В случае реализации перегрузки компонентным методом он передается в операторную функцию через указатель this. Таким образом, число аргументов такой операторной функции на 1 меньше, чем дружественной операторной функции (см. пример выше).

Перегруженная операция (бинарная или унарная), левым операндом которой является переменная стандартного типа (int, float и пр.), а не объект класса, НЕ МОЖЕТ быть компонентной функцией класса.

Например, запись вида x+1 (x – объект некоторого класса) интерпретируется компилятором как вызов операторной функции

x.operator+(1) или operator+(x,1)

Если поменять операнды местами, то при интерпретации записи 1+х (х – объект некоторого класса) возникнет ошибка:

1.operator+(x) – ошибка (попытка вызова перегруженного оператора переменной стандартного типа).

operator+(1,x) – допустимый вариант.

 

Из указанного выше примера следует важное правило: перегрузка бинарной операции, первым операндом которой является переменна стандартного типа

<переменная_стандартного_типа> @ <объект класса>

допустима только с помощью дружественных (внешних) операторных функций.

 

Перегрузка унарной операции $ также может быть реализована при помощи дружественной или компонентной функций. В первом случае функция будет иметь один аргумент, во втором – ноль (операнд будет передан в функцию через указатель this).

Так запись вида $x (x – объект некоторого класса, $ - унарная операция) будет интерпретироваться как

operator$(x) – если перегрузка унарной операции $ реализована в виде дружественной функции (у функции один аргумент);

x.operator$() – если перегрузка унарной операции $ реализована в виде компонентной функции класса (у функции нет аргументов).

 

Перегрузка постфиксных и префиксных форм унарных операций ++ и -- имеют существенное отличие. В языке С++ принято правило, согласно которому перегрузка префиксных операций ++ и -- не отличается от перегрузки других унарных операций. Операторная функция перегрузки постфиксных форм этих операций включает в себя дополнительный аргумент типа int. В случае перегрузки дружественной функцией, операторная функция имеет два аргумента (первый – «класс» или «ссылка на класс», а второй - int); у компонентной операторной функции будет только один аргумент типа int. Введенный в состав функции дополнительный вспомогательный целочисленный аргумент будет иметь произвольное значение, неоказывающее влияние на итог операции.

friend clname& operator++(clname&); // префиксный ++

friend clname& operator--(clname&, int); // постфиксный –-

clname& operator--(); // префиксный –-

clname& operator++(int); // постфиксный ++

 

Следует отметить, что операторная функция может быть вызвана в явном виде, по имени, как и любая другая функция класса.

class clname {…}

int main()

{

clname x(10), y(20), z1, z2;

z1=x+y; // вызов перегруженной операции +

z2=x.operator+(y); // явный вызов операторной функции operator+

}

 

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

Операторы = (присваивание) и & (получение адреса) по умолчанию включаются в спецификацию класса, если они явно не определены пользователем.

Некоторые операции, такие как = (операция присваивания), -> (операция косвенного выбора компонента класса), [] (операция индексирования массива), () (операция вызова функции), можно переопределить только как методы класса.

 

Перегрузка операции присваивания =

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

Создаваемый в классе по умолчанию перегруженный оператор присваивания должен иметь в качестве кода возврата ссылку на объект класса и в качестве аргумента константную ссылку на объект класса. Код возврата (return) в коде оператора присваивания должен возвращать *this.

Перегруженный по умолчанию оператор присваивания реализует прямое последовательное присваивание одноименных полей одного объекта, другому объекту того же класса. Если класс содержит поля-указатели на динамически выделяемую память, то для корректной работы он должен быть явно переопределен. В противном случае, после присваивания оба объекта будут ссылаться на один и тот же участок памяти.

Иногда может возникнуть ситуация, когда требуется запретить присваивание объектов класса во внешних функциях (создаваемый по умолчанию перегруженный оператор присваивания позволяет это сделать). Для этого следует явно переопределить перегружающую оператор присваивания операторную функцию в приватной области определения класса.

Операция присваивания имеет правую ассоциативность. Это означает, что запись а=b=c, будет интерпретироваться как a=(b=с)). Правая ассоциативность этой операции обеспечивается возвратом ссылки на объект класса, от имени которого вызывается оператор-функция operator= и который указан с левой стороны от него.

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-04-20 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: