Советы по реализации перегрузки операторов



 

Действие перегруженного оператора применительно к классу, для которого он определяется, не обязательно должно иметь отношение к стандартному действию этого оператора применительно к встроенным С++‑типам. Например, операторы "<<" и ">>" , применяемые к объектам cout и cin , имеют мало общего с аналогичными операторами, применяемыми к значениям целочисленного типа. Но для улучшения структурированности и читабельности программного кода создаваемый перегруженный оператор должен по возможности отражать исходное назначение того или иного оператора. Например, оператор "+" , перегруженный для класса three_d , концептуально подобен оператору "+" , определенному для целочисленных типов. Ведь вряд ли есть логика в определении для класса, например, оператора "+" , который по своему действию больше напоминает оператор деления (/ ). Таким образом, основная идея создания перегруженного оператора – наделить его новыми (нужными для вас) возможностями, которые, тем не менее, связаны с его первоначальным назначением.

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

 

 

Оператор ".*" – это оператор специального назначения (он рассматривается ниже в этой книге).

 

О значении порядка операндов

 

Перегружая бинарные операторы, помните, что во многих случаях порядок следования операндов имеет значение. Например, выражение А+В коммутативно, а выражение А‑В – нет. (Другими словами, А ‑ В не то же самое, что В ‑ А !) Следовательно, реализуя перегруженные версии некоммутативных операторов, необходимо помнить, какой операнд стоит слева от символа операции, а какой – справа от него. Например, в следующем фрагменте кода демонстрируется перегрузка оператора вычитания для класса three_d.

 

 

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

 

Перегрузка операторов с использованием функций‑не членов класса

 

Бинарные операторные функции, которые не являются членами класса, имеют два параметра, а унарные (тоже не члены) – один.

Перегрузку оператора для класса можно реализовать и с использованием функции, не являющейся членом этого класса. Такие функции часто определяются "друзьями" класса. Как упоминалось выше, функции‑не члены (в том числе и функции‑"друзья" ) не имеют указателя this . Следовательно, если для перегрузки бинарного оператора используется функция‑"друг" , явным образом передаются оба операнда. Если же с помощью функции‑"друга" перегружается унарный оператор, операторной функции передается один оператор. С использованием функций‑не членов класса нельзя перегружать такие операторы:

 

 

Например, в следующей программе для перегрузки оператора "+" вместо функции‑члена используется функция‑"друг".

 

 

Как видите, операторной функции operator+() теперь передаются два операнда. Левый операнд передается параметру op1 , а правый – параметру ор2 .

Во многих случаях при перегрузке операторов с помощью функций‑"друзей" нет никакого преимущества по сравнению с использованием функций‑членов класса. Однако возможна ситуация (когда нужно, чтобы слева от бинарного оператора стоял объект встроенного типа), в которой функция‑"друг" оказывается чрезвычайно полезной. Чтобы понять это, рассмотрим следующее. Как вы знаете, указатель на объект, который вызывает операторную функцию‑член, передается с помощью ключевого слова this . При использовании бинарного оператора функцию вызывает объект, расположенный слева от него. И это замечательно при условии, что левый объект определяет заданную операцию. Например, предположим, что у нас есть некоторый объект ob , для которого определена операция сложения с целочисленным значением, тогда следующая запись представляет собой вполне допустимое выражение.

 

 

Поскольку объект ob стоит слева от оператора "+" , он вызывает перегруженную операторную функцию, которая (предположительно) способна выполнить операцию сложения целочисленного значения с некоторым элементом объекта ob . Но эта инструкция работать не будет.

 

 

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

Решение описанной проблемы состоит в перегрузке оператора "+" с использованием двух функций‑"друзей" В этом случае операторной функции явным образом передаются оба аргумента, и она вызывается подобно любой другой перегруженной функции, т.е. на основе типов ее аргументов. Одна версия операторной функции operator+() будет обрабатывать аргументы объект + int‑значение , а другая – аргументы int‑значение + объект . Перегрузка оператора "+" (или любого другого бинарного оператора) с использованием функций‑"друзей" позволяет ставить значение встроенного типа как справа, так и слева от оператора. Реализация этого решения показана в следующей программе.

 

 

Как видите, операторная функция operator+() перегружается дважды, позволяя тем самым предусмотреть два возможных способа участия целого числа и объекта типа CL в операции сложения.

 

Использование функций‑"друзей" для перегрузки унарных операторов

 

С помощью функций‑"друзей" можно перегружать и унарные операторы. Но это потребует от программиста дополнительных усилий. Для начала мысленно вернемся к исходной версии перегруженного оператора "++" , определенной для класса three_d и реализованной в виде функции‑члена. Для удобства приведем код этой операторной функции здесь.

 

 

Как вы знаете, каждая функция‑член получает (в качестве неявно переданного) аргумент this , который является указателем на объект, вызвавший эту функцию. При перегрузке унарного оператора с помощью функции‑члена аргументы явным образом не передаются вообще. Единственным аргументом, необходимым в этой ситуации, является неявный указатель на вызывающий объект. Любые изменения, вносимые в данные объекта, повлияют на объект, для которого была вызвана эта операторная функция. Следовательно, при выполнении инструкции х++ (в предыдущей функции) будет инкрементирован член х вызывающего объекта.

В отличие от функций‑членов , функции‑не члены (в том числе и "друзья" класса) не получают указатель this  и, следовательно, не имеют доступа к объекту, для которого они были вызваны. Но мы знаем, что "дружественной" операторной функции операнд передается явным образом. Поэтому попытка создать операторную функцию‑"друга" operator++() в таком виде успехом не увенчается.

 

 

Эта функция неработоспособна, поскольку только копия объекта, активизировавшего вызов функции operator++() , передается функции через параметр op1 . Таким образом, изменения в теле функции operator++() не повлияют на вызывающий объект, они изменяют только локальный параметр.

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

Если для перегрузки операторов инкремента или декремента используется функция‑"друг" , ее префиксная форма принимает один параметр (который и является операндом), а постфиксная форма – два параметра (вторым является целочисленное значение, которое не используется).

Ниже приведен полный код программы обработки трехмерных координат, в которой используется операторная функция‑"друг" operator++() . Обратите внимание на то, что перегруженными являются как префиксная, так и постфиксная формы операторов инкремента.

 

 

Узелок на память. Для реализации перегрузки операторов следует использовать функции‑члены. Функции‑"друзья" используются в C++ в основном для обработки специальных ситуаций.

 


Дата добавления: 2018-09-22; просмотров: 377; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!