Перегрузка операторов в C++
Желание написать данную статью появилось после прочтения поста Перегрузка C++ операторов, потому что в нём не были раскрыты многие важные темы.
Самое главное, что необходимо помнить — перегрузка операторов, это всего лишь более удобный способ вызова функций, поэтому не стоит увлекаться перегрузкой операторов. Использовать её следует только тогда, когда это упростит написание кода. Но, не настолько, чтобы это затрудняло чтение. Ведь, как известно, код читается намного чаще, чем пишется. И не забывайте, что вам никогда не дадут перегрузить операторы в тандеме со встроенными типами, возможность перегрузки есть только для пользовательских типов/классов.
Синтаксис перегрузки
Синтаксис перегрузки операторов очень похож на определение функции с именем operator@, где @ — это идентификатор оператора (например +, -, <<, >>). Рассмотрим простейший пример:
В данном случае, оператор оформлен как член класса, аргумент определяет значение, находящееся в правой части оператора. Вообще, существует два основных способа перегрузки операторов: глобальные функции, дружественные для класса, или подставляемые функции самого класса. Какой способ, для какого оператора лучше, рассмотрим в конце топика.
В большинстве случаев, операторы (кроме условных) возвращают объект, или ссылку на тип, к которому относятся его аргументы (если типы разные, то вы сами решаете как интерпретировать результат вычисления оператора).
Перегрузка унарных операторов
Рассмотрим примеры перегрузки унарных операторов для определенного выше класса Integer. Заодно определим их в виде дружественных функций и рассмотрим операторы декремента и инкремента:
Теперь вы знаете, как компилятор различает префиксные и постфиксные версии декремента и инкремента. В случае, когда он видит выражение ++i, то вызывается функция operator++(a). Если же он видит i++, то вызывается operator++(a, int). То есть вызывается перегруженная функция operator++, и именно для этого используется фиктивный параметр int в постфиксной версии.
Бинарные операторы
Рассмотрим синтаксис перегрузки бинарных операторов. Перегрузим один оператор, который возвращает l-значение, один условный оператор и один оператор, создающий новое значение (определим их глобально):
Во всех этих примерах операторы перегружаются для одного типа, однако, это необязательно. Можно, к примеру, перегрузить сложение нашего типа Integer и определенного по его подобию Float.
Аргументы и возвращаемые значения
- Если аргумент не изменяется оператором, в случае, например унарного плюса, его нужно передавать как ссылку на константу. Вообще, это справедливо для почти всех арифметических операторов (сложение, вычитание, умножение. )
- Тип возвращаемого значения зависит от сути оператора. Если оператор должен возвращать новое значение, то необходимо создавать новый объект (как в случае бинарного плюса). Если вы хотите запретить изменение объекта как l-value, то нужно возвращать его константным.
- Для операторов присваивания необходимо возвращать ссылку на измененный элемент. Также, если вы хотите использовать оператор присваивания в конструкциях вида (x=y).f(), где функция f() вызывается для для переменной x, после присваивания ей y, то не возвращайте ссылку на константу, возвращайте просто ссылку.
- Логические операторы должны возвращать в худшем случае int, а в лучшем bool.
Оптимизация возвращаемого значения
При создании новых объектов и возвращении их из функции следует использовать запись как для вышеописанного примера оператора бинарного плюса.
Честно говоря, не знаю, какая ситуация актуальна для C++11, все рассуждения далее справедливы для C++98.
На первый взгляд, это похоже на синтаксис создания временного объекта, то есть как будто бы нет разницы между кодом выше и этим:
Но на самом деле, в этом случае произойдет вызов конструктора в первой строке, далее вызов конструктора копирования, который скопирует объект, а далее, при раскрутке стека вызовется деструктор. При использовании первой записи компилятор изначально создаёт объект в памяти, в которую нужно его скопировать, таким образом экономится вызов конструктора копирования и деструктора.
Особые операторы
В C++ есть операторы, обладающие специфическим синтаксисом и способом перегрузки. Например оператор индексирования []. Он всегда определяется как член класса и, так как подразумевается поведение индексируемого объекта как массива, то ему следует возвращать ссылку.
Оператор запятая
В число «особых» операторов входит также оператор запятая. Он вызывается для объектов, рядом с которыми поставлена запятая (но он не вызывается в списках аргументов функций). Придумать осмысленный пример использования этого оператора не так-то просто. Хабраюзер AxisPod в комментариях к предыдущей статье о перегрузке рассказал об одном.
Оператор разыменования указателя
Перегрузка этих операторов может быть оправдана для классов умных указателей. Этот оператор обязательно определяется как функция класса, причём на него накладываются некоторые ограничения: он должен возвращать либо объект (или ссылку), либо указатель, позволяющий обратиться к объекту.
Оператор присваивания
Как можно заметить, в начале функции производится проверка на самоприсваивание. Вообще, в данном случае самоприсваивание безвредно, но ситуация не всегда такая простая. Например, если объект большой, можно потратить много времени на ненужное копирование, или при работе с указателями.
Неперегружаемые операторы
Некоторые операторы в C++ не перегружаются в принципе. По всей видимости, это сделано из соображений безопасности.
- Оператор выбора члена класса «.».
- Оператор разыменования указателя на член класса «.*»
- В С++ отсутствует оператор возведения в степень (как в Fortran) «**».
- Запрещено определять свои операторы (возможны проблемы с определением приоритетов).
- Нельзя изменять приоритеты операторов
Рекомендации к форме определения операторов
Как мы уже выяснили, существует два способа операторов — в виде функции класса и в виде дружественной глобальной функции.
Роб Мюррей, в своей книге C++ Strategies and Tactics определил следующие рекомендации по выбору формы оператора:
| Оператор | Рекомендуемая форма |
| Все унарные операторы | Член класса |
| = () [] -> ->* | Обязательно член класса |
| += -= /= *= ^= &= |= %= >>= <<= | Член класса |
| Остальные бинарные операторы | Не член класса |
Почему так? Во-первых, на некоторые операторы изначально наложено ограничение. Вообще, если семантически нет разницы как определять оператор, то лучше его оформить в виде функции класса, чтобы подчеркнуть связь, плюс помимо этого функция будет подставляемой (inline). К тому же, иногда может возникнуть потребность в том, чтобы представить левосторонний операнд объектом другого класса. Наверное, самый яркий пример — переопределение << и >> для потоков ввода/вывода.
Глава 13: Перегрузка операторов
В C++ операторы можно перегружать для «классовых» типов, определяемых программистом. Принципиальный выигрыш от перегрузки операторов состоит в том, что она позволяет органично интегрировать новые типы данных в среду программирования.
Перегружая оператор, можно определить его значение для конкретного класса. Например, класс, который определяет связный список, может использовать оператор «+» для добавления объекта к списку. Класс, которые реализует стек, может использовать оператор «+» для записи объекта в стек. В каком-нибудь другом классе тот же оператор «+» мог бы служить для совершенно иной цели. При перегрузке оператора ни одно из оригинальных его значений не теряется. Перегруженный оператор (в своем новом качестве) работает как совершенно новый оператор. Поэтому перегрузка оператора «+» для обработки, например, связного списка не приведет к изменению его функции (т.е. операции сложения) по отношению к целочисленным значениям.
Перегрузка операторов тесно связана с перегрузкой функций. Чтобы перегрузить оператор, необходимо определить значение новой операции для класса, к которому она будет применяться. Для этого создается функция operator (операторная функция), которая определяет действие этого оператора. Общий формат функции operator таков.
Операторы перегружаются с помощью функции operator.
Здесь перегружаемый оператор обозначается символом «#» , а элемент тип представляет собой тип значения, возвращаемого заданной операцией. И хотя он в принципе может быть любым, тип значения, возвращаемого функцией operator , часто совпадает с именем класса, для которого перегружается данный оператор. Такая корреляция облегчает использование перегруженного оператора в составных выражениях. Как будет показано ниже, конкретное значение элемента список_аргументов определяется несколькими факторами.
Операторная функция может быть членом класса или не быть им. Операторные функции, не являющиеся членами класса, часто определяются как его «друзья». Операторные функции-члены и функции-не члены класса различаются по форме перегрузке. Каждый из вариантов мы рассмотрим в отдельности.
Перегрузка операторов с использованием функций-членов
Начнем с простого примера. В следующей программе создается класс three_d , который поддерживает координаты объекта в трехмерном пространстве. Для класса three_d перегружаются операторы «+» и » p42 ft3″>// Перегрузка операторов с помощью функций-членов.
using namespace std;
int x, y, z; // 3-мерные координаты
three_d(int i, int j, int k)
three_d operator+(three_d op2); // Операнд op1 передается неявно.
three_d operator=(three_d op2); // Операнд op1 передается неявно.
three_d three_d::operator+(three_d op2)
temp.x = x + op2.x; // Операции сложения целочисленных
temp.у = у + ор2.у; // значений сохраняют оригинальный
temp.z = z + op2.z; // смысл.
// Перегрузка оператора присваивания.
three_d three_d::operator=(three_d op2)
x = op2.x; // Операции присваивания целочисленных
у = ор2.у; // значений сохраняют оригинальный
// Отображение координат X, Y, Z.
three_d a(1, 2, 3), b(10, 10, 10), c;
c=a+b; // сложение объектов а и b
c=a+b+c; // сложение объектов a, b и с
c=b=a; // демонстрация множественного присваивания
При выполнении эта программа генерирует такие результаты.
Исследуя код этой программы, вы, вероятно, удавились, увидев, что обе операторные функции имеют только по одному параметру, несмотря на то, что они перегружают бинарные операции. Это, на первый взгляд, «вопиющее» противоречие можно легко объяснить. Дело в том, что при перегрузке бинарного оператора с использованием функциичлена ей передается явным образом только один аргумент. Второй же неявно передается через указатель this . Таким образом, в строке
под членом х подразумевается член this->x , т.е. член х связывается с объектом, который вызывает данную операторную функцию. Во всех случаях неявно передается объект, указываемый слева от символа операции, который стал причиной вызова операторной функции. Объект, располагаемый с правой стороны от символа операции, передается этой функции в качестве аргумента. В общем случае при использовании функции-члена для перегрузки унарного оператора параметры не используются вообще, а для перегрузки бинарного — только один параметр. (Тернарный оператор «?» перегружать нельзя.) В любом случае объект, который вызывает операторную функцию, неявно передается через указатель this .
Чтобы понять, как работает механизм перегрузки операторов, рассмотрим внимательно предыдущую программу, начиная с перегруженного оператора «+» . При обработке двух объектов типа three_d оператором «+» выполняется сложение значений соответствующих координат, как показано в функции operator+() . Но заметьте, что эта функция не модифицирует значение ни одного операнда. В качестве результата операции эта функция возвращает объект типа three_d , который содержит результаты попарного сложения координат двух объектов. Чтобы понять, почему операция «+» не изменяет содержимое ни одного из объектов-участников, рассмотрим стандартную арифметическую операцию сложения, примененную, например, к числам 10 и 12 . Результат операции 10+12 равен 22 , но при его получении ни 10 , ни 12 не были изменены. Хотя не существует правила, которое бы не позволяло перегруженному оператору изменять значение одного из его операндов, все же лучше, чтобы он не противоречил общепринятым нормам и оставался в согласии со своим оригинальным назначением.
Обратите внимание на то, что функция operator+() возвращает объект типа three_d . Несмотря на то что она могла бы возвращать значение любого допустимого в C++ типа, тот факт, что она возвращает объект типа three_d , позволяет использовать оператор «+» в таких составных выражениях, как a+b+с . Часть этого выражения, а+Ь , генерирует результат типа three_d , который затем суммируется с объектом с . И если бы эта часть выражения генерировала значение иного типа (а не типа three_d ), такое составное выражение попросту не работало бы.
В отличие от оператора «+» , оператор присваивания приводит к модификации одного из своих аргументов. (Прежде всего, это составляет саму суть присваивания.) Поскольку функция operator=() вызывается объектом, который расположен слева от символа присваивания ( = ), именно этот объект и модифицируется в результате операции присваивания. После выполнения этой операции значение, возвращаемое перегруженным оператором, содержит объект, указанный слева от символа присваивания. (Такое положение вещей вполне согласуется с традиционным действием оператора » p143 ft3″>а = b = с = d;
необходимо, чтобы операторная функция operator=() возвращала объект, адресуемый указателем this , и чтобы этот объект располагался слева от оператора » ft14″>this .
Узелок на память. Если для перегрузки бинарного оператора используется функциячлен, объект, стоящий слева от оператора, вызывает операторную функцию и передается
ей неявно через указатель this. Объект, расположенный справа от оператора, передается операторной функции как параметр.
Использование функций-членов для перегрузки унарных операторов
Можно также перегружать такие унарные операторы, как «++» , «—» , или унарные «-» и «+» . Как упоминалось выше, при перегрузке унарного оператора с помощью функции-члена операторной функции ни один объект не передается явным образом. Операция же выполняется над объектом, который генерирует вызов этой функции через неявно переданный указатель this . Например, рассмотрим расширенную версию предыдущего примера программы. В этом варианте для объектов типа three_d определяется операция инкремента.
C++. Перегрузка операторов в C++. Операторная функция. Ключевое слово operator. Перегрузка базовых арифметических операторов
Перегрузка операторов в C++. Операторная функция. Ключевое слово operator . Перегрузка базовых арифметических операторов + , – , * , / . Примеры реализации встроенных операторных функций
Данная тема отображает возможности языка C++ по реализации «перегрузки» операторов. Не все современные языки программирования поддерживают перегрузку операторов. Хорошое понимание процесса программирования перегруженных операторов есть показателем профессиональности и высокого мастерства современного программиста.
Перед рассмотрением данной темы рекомендуется ознакомиться со следующей темой:
Содержание
- 1. Что такое унарные и бинарные операторы?
- 2. В чем состоит суть перегрузки операторов? Что такое операторная функция?
- 3. Какими способами можно реализовать операторную функцию для заданного класса? Какие существуют разновидности операторных функций?
- 4. Общая форма операторной функции, которая реализована в классе. Ключевое слово operator
- 5. Пример перегрузки унарных и бинарных операторов для класса, который содержит одиночные данные. Операторная функция реализована внутри класса
- 6. Пример перегрузки оператора ‘ * ‘, обрабатывающего класс, который содержит массив вещественных чисел. Операторная функция реализована внутри класса
- 7. Пример суммирования двух массивов. Операторная функция operator+() размещается внутри класса
- 8. Какие ограничения накладываются на перегруженные операторы?
- 9. Какие операторы нельзя перегружать?
- 10. Объекты каких типов может возвращать операторная функция? Примеры операторных функций, которые возвращают объекты разных типов
- 11. Можно ли изменять значения операндов в операторной функции?
- 12. Можно ли реализовать операторные функции в классе, которые перегружают одинаковый оператор, получают одинаковые параметры но возвращают разные значения?
- 13. Можно ли реализовать две и более операторных функции в классе, которые перегружают одинаковый оператор и получают разные (отличные между собой) параметры?
Поиск на других ресурсах:
1. Что такое унарные и бинарные операторы?
Различают три основных вида операторов: унарные, бинарные и n -арные ( n >2).
Унарные операторы – это операторы, которые для вычислений требуют одного операнда, который может размещаться справа или слева от самого оператора.
Примеры унарных операторов:
Бинарные операторы – это операторы, которые для вычисления требуют двух операндов.
Пример. Ниже отображены фрагменты выражений с бинарными операторами + , – , % , *
n -арные операторы для вычислений требуют более двух операндов. В языке C++ есть тернарная операция ?: , которая для своей работы требует три операнда. Более подробно об этой тернарной операции ?: описывается здесь.
2. В чем состоит суть перегрузки операторов? Что такое операторная функция?
Язык C++ имеет широкие возможности для перегрузки большинства операторов. Перегрузка оператора означает использование оператора для оперирования объектами классов. Перегрузка оператора – способ объявления и реализации оператора таким образом, что он обрабатывает объекты конкретных классов или выполняет некоторые другие действия. При перегрузке оператора в классе вызывается соответствующая операторная функция (operator function), которая выполняет действия, которые касаются данного класса.
Если оператор «перегружен», то его можно использовать в других методах в обычном для него виде. Например, команды поэлементного суммирования двух массивов a1 и a2
лучше вызвать более естественном способом:
В данном примере оператор ‘+’ считается перегруженным.
3. Какими способами можно реализовать операторную функцию для заданного класса? Какие существуют разновидности операторных функций?
Для заданного класса операторную функцию в классе можно реализовать:
- внутри класса. В этом случае, операторная функция есть методом класса;
- за пределами класса. В этом случае операторная функция объявляется за пределами класса как «дружественная» (с ключевым словом friend ). Более подробно о реализации «дружественных» функций за пределами класса описывается здесь .
4. Общая форма операторной функции, которая реализована в классе. Ключевое слово operator
Общая форма операторной функции, реализованной в классе, имеет следующий вид:
- return_type – тип значения, которое возвращается операторной функцией;
- ClassName – имя класса, в котором реализована операторная функция;
- operator # – ключевое слово, определяющее операторную функцию в классе. Символ # заменяется оператором языка C++, который перегружается. Например, если перегружается оператор + , то нужно указать operator+ ;
- argument_list – список параметров, которые получает операторная функция. Если перегружается бинарный оператор, то argument_list содержит один аргумент. Если перегружается унарный оператор, то список аргументов пустой.
5. Пример перегрузки унарных и бинарных операторов для класса, который содержит одиночные данные. Операторная функция реализована внутри класса
Объявляется класс Point , реализующий точку на координатной плоскости. В классе реализованы:
- две внутренние переменные x , y , которые есть координатами точки;
- два конструктора класса;
- методы доступа к внутренним переменным класса GetX() , GetY() , SetX() , SetY() ;
- две операторные функции operator+() и operator-() .
Как видно из вышеприведенного кода, операторная функция operator+() получает один параметр. Это значит, что эта функция реализует бинарный оператор ‘+’ . Этот параметр соответствует операнду, который размещается в правой части бинарного оператора ‘+’ . Операнд, который размещается в левой части оператора ‘+’ передается операторной функции неявно с помощью указателя this данного класса.
Вызов операторной функции осуществляет объект, который размещается в левой части оператора присваивания.
Демонстрация использования перегруженных операторов класса Point в другом методе:
В вышеприведенном коде, в операции суммирования ‘+’ объект P1 вызывает операторную функцию. То есть, фрагмент строки
Реализовать операторную функцию operator+() в классе можно и по другому
В вышеприведенной функции в операторе return создается временный объект путем вызова конструктора с двумя параметрами, реализованного в классе. Если (в данном случае) из тела класса убрать конструктор с двумя параметрами
то вышеприведенный вариант функции operator+() работать не будет, так как для создания объекта типа Point эта функция использует конструктор с двумя параметрами. В этом случае компилятор выдаст сообщение
что значит: нет метода (конструктора) Point::Point() принимающего 2 аргумента.
6. Пример перегрузки оператора ‘*’ , обрабатывающего класс, который содержит массив вещественных чисел. Операторная функция реализована внутри класса
В примере реализуется операторная функция operator*() , которая умножает поэлементно значения внутренних массивов объектов класса ArrayFloat . Если размер массивов неодинаков, то умножается только то число элементов, которое есть минимальным между двумя размерами массивов.
Использование класса ArrayFloat в другом методе
7. Пример суммирования двух массивов. Операторная функция operator+() размещается внутри класса
Задан класс ArrayFloat , реализующий динамический массив чисел типа float . В классе реализованы:
- внутренние переменные size , A , описывающие размер массива и сам массив;
- два конструктора, инициализирующие начальными значениями элементы массива;
- конструктор копирования;
- методы доступа GetSize() , SetSize() , GetAi() , SetAi() , которые реализуют доступ к внутренним переменным массива с соответствующими операциями (выделение памяти, проверка на допустимые границы);
- операторная функция operator=() , которая реализует копирование объектов;
- операторная функция operator+() , которая реализует перегрузку оператора ‘+’ для массивов типа ArrayFloat . Операторная функция добавляет поэлементно значения массивов, которые являются операндами операции ‘+’ .
- деструктор.
Ниже продемонстрировано использование класса ArrayFloat и операторной функции operator+() этого класса.
8. Какие ограничения накладываются на перегруженные операторы?
На использование перегруженных операторов накладываются следующие ограничения:
- при перегрузке оператора нельзя изменить приоритет этого оператора;
- нельзя изменить количество операндов оператора. Однако, в коде операторной функции можно один из параметров (операндов) не использовать;
- нельзя перегружать операторы :: , . , * , ?: ;
- нельзя вызвать операторную функцию с аргументами по умолчанию. Исключение – операторная функция вызова функции operator()() .
9. Какие операторы нельзя перегружать?
Нельзя перегружать следующие операторы:
- :: – расширение области видимости;
- . (точка) – доступ к члену структуры или класса;
- * – доступ по указателю;
- ?: – тернарная операция.
10. Объекты каких типов может возвращать операторная функция? Примеры операторных функций, которые возвращают объекты разных типов
Операторная функция может возвращать объекты любых типов. Наиболее часто операторная функция возвращает объект типа класса, в котором она реализованная или с которыми она работает.
Пример. Задан класс Complex , в котором перегружаются два оператора:
- унарный оператор ‘+’ , возвращающий модуль комплексного числа (тип double );
- бинарный оператор ‘+’ , возвращающий сумму комплексных чисел. Операторная функция возвращает объект типа Complex ;
- бинарный оператор ‘+’ , который добавляет к комплексному числу некоторое вещественное число. В этом случае операторная функция получает входным параметром вещественное число и возвращает объект типа Complex .
Текст класса следующий:
Далее демонстрируется использование класса Complex и перегруженных операторных функций в некотором другому методе
11. Можно ли изменять значения операндов в операторной функции?
Да, можно. Однако такие действия не являются полезными с точки зрения здравого смысла. Так, например, операция умножения
не изменяет значения своих операндов 6 и 9. Результат равен 54. Если операторная функция operator*() будет изменять значения своих операндов, то это может привести к невидимым ошибкам в программах, поскольку программист по привычке, будет считать, что значения операндов есть неизменными.
12. Можно ли реализовать операторные функции в классе, которые перегружают одинаковый оператор, получают одинаковые параметры но возвращают разные значения?
Нет, нельзя. Операторная функция не может иметь несколько реализаций в классе с одинаковой сигнатурой параметров (когда типы и количество параметров совпадают). В случае нарушения этого правила компилятор выдает ошибку:
Например. Нельзя в классе перегружать оператор ‘+’ так как показано ниже
Это правило касается любых функций класса.
13. Можно ли реализовать две и более операторных функции в классе, которые перегружают одинаковый оператор и получают разные (отличные между собой) параметры?
Да, можно. В п. 10 реализован класс Complex , в котором реализованы две операторные функции, которые перегружают оператор ‘+’ разными способами. Эти функции отличаются входящими параметрами.
Перегрузка операций
Кроме перегрузки функций С++ позволяет организовать перегрузку операций. Механизм перегрузки операций позволяет обеспечить более традиционную и удобную запись действий над объектами. Для перегрузки встроенных операторов используется ключевое слово operator .
Синтаксически перегрузка операций осуществляется следующим образом:
где @ — знак перегружаемой операции (-, +, * и т.д.),
тип — тип возвращаемого значения.
Тип возвращаемого значения должен быть отличным от void , если необходимо использовать перегруженную операцию внутри другого выражения.
Например, функция перемножения матрицы и вектора может быть записана следующим образом.
Любой перегруженный оператор можно вызвать с использованием функциональной формы записи (функции-операции):
Функция-операция описывается и может вызываться так же, как любая другая функция. Использование операции – это лишь сокращенная запись явного вызова функции операции.
Имеется два способа описания функции, соответствующей переопределяемой операции:
- если функция задается как обычный метод класса, то первым операндом ее является объект класса, указатель на который передается неявным указателем this ;
- если первый операнд переопределяемой операции не является объектом некоторого класса, либо требуется передавать в качестве операнда не указатель на объект, а некоторое значение, то соответствующая функция должна быть определена как дружественная классу с полным списком аргументов.
Можно описывать функции, определяющие значения следующих операций:
Операции, не допускающие перегрузки:
- . прямой выбор члена объекта класса;
- .* обращение к члену через указатель на него;
- ? : условная тернарная операция;
- :: операция указания области видимости (разрешение контекста);
- sizeof операция вычисления размера в байтах;
- # препроцессорная операция.
Правила перегрузки операций
- Язык C++ не допускает определения для операций нового лексического символа, кроме уже определенных в языке. Например, нельзя определить в качестве знака операции @ .
- Не допускается перегрузка операций для встроенных типов данных. Нельзя, например, переопределить операцию сложения целых чисел:
Перегрузка унарной операции
Если унарная операция перегружается как функция-член, то она не должна иметь аргументов, так как в этом случае ей передается неявный аргумент-указатель this на текущий объект.
Если унарная операция перегружается дружественной функцией, то она должна иметь один аргумент – объект, для которого она выполняется.
Таким образом, для любой унарной операции @ aa@ или @aa может интерпретироваться или как aa.operator@() , или как operator @(aa) .
Если определена и та, и другая, то и aa@ и @aa являются ошибками.
Перегрузка бинарной операции
Если бинарная операция перегружается с использованием метода класса, то в качестве своего первого аргумента она получает неявно переданную переменную класса (указатель this на объект), а в качестве второго — аргумент из списка параметров. То есть, фактически бинарная операция, перегружаемая методом класса, имеет один аргумент (правый операнд), а левый передается неявно через указатель this .
Если бинарная операция перегружается дружественной функцией, то в списке параметров она должна иметь оба аргумента:
Результат выполнения
Для каждой комбинации типов операндов в переопределяемой операции необходимо ввести отдельную функцию, т.е. компилятор не может производить перестановку операндов местами, даже если базовая операция допускает это. Например, если необходима операция сложения комплексного и вещественного чисел:
то необходимо переопределить операцию сложения дважды:
Перегрузка операций индексирования и вызова функции
Переопределение операции () позволяет использовать синтаксис вызова функции применительно к объекту класса (имя объекта с круглыми скобками). Количество операндов в скобках может быть любым. Переопределение операции [] позволяет использовать синтаксис доступа к элементам массива (имя объекта с квадратными скобками).
Результат выполнения 
Перегрузка операции присваивания
Любой конструктор вызывается явно либо неявно в том случае, если необходимо создать новый объект какого-либо класса.
Рассматривая на примере создание нового объекта и его инициализацию, мы использовали конструктор копии. До проведения инициализации существовал только один объект. Второй был создан и инициализирован в результате работы конструктора копии. Однако если бы существовало несколько объектов одного типа, и была бы необходимость в присваивании значений одного объекта элементам другого, то никакой из конструкторов не вызывается, так как объект уже был создан. При выполнении операции присваивания по умолчанию копирование значений происходит «поверхностно», но такое копирование не всегда допустимо. Например, недопустимо копирование массивов или указателей.
Если необходимо осуществить присваивание, но поведение операции присваивания по умолчанию не устраивает, то операция присваивания может быть перегружена.
Дополним приведенный выше пример реализации класса String перегруженным оператором присваивания:
Результат выполнения: 
Перегрузка операций выделения памяти
Операции создания и уничтожения объектов в динамической памяти могут быть переопределены следующим образом
где void * – указатель на область памяти, выделяемую под объект,
size – размер объекта в байтах,
size_t – тип размерности области памяти, int или long int .
Переопределение этих операций позволяет написать собственное распределение памяти для объектов класса.