46
class String {
// Как раньше
public:
String& operator=(const String&);
// Нормальный вариант
String&
operator=(char*);
//
Перегруженный вариант
String&
operator=(int);
//
Вызывает atoi()
};
В показанном фрагменте создается несколько перегруженных
вариантов оператора
=
для различных
типов данных в правой части выражения. Второй вариант позволяет избежать конструирования
временного объекта
String
из
char*
лишь для того, чтобы присвоить его объекту в левой части.
Третий вариант выполняет преобразование другого рода.
Тем не менее, лишь первый вариант
перегружает (то есть заменяет) версию оператора по умолчанию.
Перегрузка операторов
Одна из приятных особенностей C++ — возможность расширения смысла операторов. Это упрощает
чтение программы, поскольку вам уже не придется изобретать дурацкие имена функций вроде
Add
там,
где знак
+
имеет совершенно очевидный смысл. Тем не менее, из личного опыта я знаю две проблемы,
связанные с перегруженными операторами. Во-первых, их чрезмерное применение превращает
программу в хаос. Во-вторых, большинство программистов никогда их не использует.
Приведенный
ниже список не претендует на полноту, однако он поможет подготовить поле для дальнейшего
изложения материала.
Функциональная форма операторов
Операторы (например,
+
) используются в двух вариантах: как особая синтаксическая форма или как
функция. В C++ функциональная форма всегда представляет собой ключевое слово
operator
, за
которым следует символ оператора.
class Foo {...}
Foo x, y, z;
z = x + y;
// Инфиксная (нормальная) форма
z = operator+(x, y);
// Функциональная форма (внешняя функция)
z = x.operator+(y);
// Функциональная форма (функция класса)
С концептуальной точки зрения три
последние строки эквивалентны, хотя на практике, вероятно,
оператор будет определен либо в виде внешней функции, либо в виде функции класса, но не в обоих
вариантах сразу. Для бинарных операторов знак оператора указывается между двух аргументов в
инфиксной форме. В форме внешней функции оба аргумента передаются глобальной функции. В
форме функции класса объект, которому принадлежит
вызываемый оператор, указывается слева, а
аргумент — справа от знака оператора. Унарные операторы (такие как
!
и
~
) тоже могут
перегружаться. Форма внешней функции вызывается с одним аргументом, а форма функции класса
вызывается без аргументов (операция выполняется с объектом, находящимся слева от оператора
.
или
->
).
Не разрешается перегружать встроенные операторы (например, оператор целочисленного сложения).
Чтобы обеспечить
выполнение этого условия, компилятор требует, чтобы хотя бы один аргумент
каждого перегруженного оператора относился к пользовательскому типу (обычно к классу). Выбор
ограничен операторами, уже определенными в C++. Во время долгих ночных отладок мне часто хоте-
лось создать оператор с именем
#$%^&
, но C++ на этот счет неумолим.
Перегруженные операторы наследуют приоритеты и атрибуты группировки от встроенных операторов,
поэтому вы не можете, например, изменить стандартный порядок группировки «слева направо» для
оператора
+
. Не существует ограничений на тип значения, возвращаемого перегруженным оператором,
и один оператор можно перегружать произвольное число раз при условии, что сигнатуры остаются
уникальными.