На этом шаге мы рассмотрим несколько особенностей, присущих перегрузке стандартных операций.
Механизм классов дает возможность программисту определять новые типы данных, отображающие понятия решаемой задачи. Перегрузка стандартных операций языка C++ позволяет сделать операции над объектами новых классов удобными и общепонятными. Но возникают два вопроса.
Можно ли вводить собственные обозначения для операций, не совпадающие со стандартными операциями языка C++?
И все ли операции языка C++ могут быть перегружены?
К сожалению, вводить операции с совершенно новыми обозначениями язык C++ не позволяет. Ответ на второй вопрос также отрицателен - существует несколько операций, не допускающих перегрузки. Вот их список:
Прямой выбор компонента структурированного объекта. | |
Обращение к компоненту через указатель на него. | |
Условная операция. | |
Операция указания области видимости. | |
Операция вычисления размера в байтах. | |
Препроцессорная операция. | |
Препроцессорная операция. |
Рассмотрим еще несколько важных особенностей механизма перегрузки (расширения действия) стандартных операций языка C++.
В первом случае X @ Y означает вызов X.operator @ (Y), во втором случае X @ Y означает вызов operator @ (X,Y).
В соответствии с семантикой бинарных операций "=", "[ ]", "->" операции-функции с названиями operator =, operator [ ], operator -> не могут быть глобальными функциями, а должны быть нестатическими компонентными функциями.
Для префиксной операции "$" выражение $z означает вызов компонентной функции z.operator $ () или вызов глобальной функции operator $ (z).
Для постфиксной операции выражение z$ означает либо вызов компонентной функции z.operator $ (), либо вызов глобальной функции operator $ (z).
При разборе выражения аа + 2 компилятором выполняется вызов операции-функции аа.operator + (2) или operator + (aa,2).
При разборе 2 + аа допустим вызов operator + (2,аа), но ошибочен 2.operator + (аа). Таким образом, расширение действия операции "+" на выражение
стандартный_тип + объект_класса
Например, для рассмотренного класса complex можно ввести как дружественные такие операции-функции:
complex operator + (complex x, complex у) { return complex(x.real + у.real, x.imag + y.imag); } complex operator + (double x, complex y) { return complex(x + y.real, y.imag); } complex operator + (complex x, double y) { return complex(x.real + y, x.imag); }
После этого станут допустимыми выражения в следующих операторах:
complex CC(1.0,2.0); complex ЕЕ; ЕЕ = 4.0 + CC; ЕЕ = ЕЕ + 2.0; ЕЕ = CC + ЕЕ; ЕЕ = CC + 20; // По умолчанию приведение int к double. CC = ЕЕ + 'е'; // По умолчанию приведение char к double.
Вместо использования нескольких (в нашем примере вместо трех) очень схожих операций-функций можно задачу преобразования стандартного типа в объект класса поручить конструктору. Для этого требуется только одно - необходим конструктор, формирующий объект класса по значению стандартного типа. Например, добавление в класс complex такого конструктора:
complex(double x)
{ real = x; imag = 0.0; }
позволяет удалить все дополнительные операции-функции, оставив только одну с прототипом:
friend complex operator + (complex, complex);
В этом случае целый операнд выражения 6 + ЕЕ автоматически преобразуется к типу double, а затем конструктор формирует комплексное число с нулевой мнимой частью. Далее выполняется операция-функция:
operator + (complex(double(6),double(0)), ЕЕ)
Вместо включения в класс дополнительного конструктора с одним аргументом можно в заголовке единственного конструктора ввести умалчиваемое значение второго параметра:
complex(double re, double im = 0.0) { real = re; imag = im; }
Теперь каждое выражение с операцией "+", в которое входит, кроме объекта класса complex, операнд одного из стандартных типов, будет обрабатываться совершенно верно. Однако такое умалчивание является частным решением и не для всех классов пригодно.
Можно было бы в качестве умалчиваемого значения мнимой части взять и число, отличное от нуля, но поведение объектов класса complex при сложении с данными стандартных типов оказалось бы неверным.
На следующем шаге мы продолжим подводить итоги перегрузки операций.