C++
Шрифт:
class X (* // друзья
friend X operator-(X); // унарный минус friend X operator-(X,X); // бинарный минус friend X operator-; // ошибка: нет операндов
friend X operator-(X,X,X); // ошибка: тернарная
// члены (с неявным первым параметром: this)
X* operator amp;; // унарное amp; (взятие адреса) X operator amp;(X); // бинарное amp; (операция И) X operator amp;(X,X); // ошибка: тернарное
*);
Когда операции ++ и – перегружены, префиксное использвание и постфиксное различить невозможно.
6.2.2 Предопределенный смысл операций
Относительно
Значения некоторых встроенных операций определены как равносильные определенным комбинациям других операций над тми же аргументами. Например, если a является int, то ++a оначает a+=1, что в свою очередь означает a=a+1. Такие соотншения для определяемых пользователем операций не выполняются, если только не случилось так, что пользователь сам определил их таким образом. Например, определение operator+= для тпа complex не может быть выведено из определений complex::operator+ и complex::operator=.
По историческому совпадению операции = и amp; имеют опредленный смысл для объектов классов. Никакого элегантного спсоба «не определить» эти две операции не существует. Их моно, однако, сделать недееспособными для класса X. Можно, например, описать X::operator amp;, не задав ее определения. Если где-либо будет браться адрес объекта класса X, то компновщик обнаружит отсутствие определения*. Или, другой способ, можно определить X::operator amp; так, чтобы она вызывала ошику во время выполнения.
____________________ * В некоторых системах компоновщик настолько «умен», что ругается, даже если неопределена неиспользуемая функция. В таких системах этим методом воспользоваться нельзя. (прим атора)
6.2.3 Операции и определяемые пользователем типы
Функция операция должна или быть членом, или получать в качестве параметра по меньшей мере один объект класса (фунциям, которые переопределяют операции new и delete, это длать необязательно). Это правило гарантирует, что пользовтель не может изменить смысл никакого выражения, не включающего в себя определенного пользователем типа. В часности, невозможно определить функцию, которая действует ислючительно на указатели.
Функция операция, первым параметром которой предполагется основной встроенный тип, не может быть функцией членом. Рассмотрим, например, сложение комплексной переменной aa с целым 2: aa+2, при подходящим образом описанной функции члне, может быть проинтерпретировано как aa.operator+(2), но с 2+aa это не может быть сделано, потому что нет такого класса int, для которого можно было бы определить + так, чтобы это означало 2.operator+(aa). Даже если бы такой тип был, то для
того, чтобы обработать и 2+aa и aa+2, понадобилось бы две различных функции члена. Так как компилятор не знает смысла +, определяемого пользователем, то не может предполагать, что он коммутативен, и интерпретировать 2+aa как aa+2. С этим примером могут легко справиться функции друзья.
Все функции операции по определению перегружены. Функция операция задает новый смысл операции в дополнение к встроеному определению, и может существовать несколько функций опраций с одним и тем же именем, если в типах их параметров имеются отличия, различимые для компилятора, чтобы он мог различать их при обращении (см. #4.6.7).
6.3 Определяемое пользователем преобразование типа
Приведенная во введении реализация комплексных чисел слишком ограничена, чтобы она могла устроить кого-либо, потому ее нужно расширить. Это будет в основном повторением описанных выше методов. Например:
class complex (* double re, im; public: complex(double r, double i) (* re=r; im=i; *)
friend complex operator+(complex, complex); friend complex operator+(complex, double); friend complex operator+(double, complex);
friend complex operator-(complex, complex); friend complex operator-(complex, double); friend complex operator-(double, complex); complex operator- // унарный -
friend complex operator*(complex, complex); friend complex operator*(complex, double); friend complex operator*(double, complex);
// ... *);
Теперь, имея описание complex, мы можем написать:
void f (* complex a(1,1), b(2,2), c(3,3), d(4,4), e(5,5); a = -b-c; b = c*2.0*c; c = (d+e)*a; *)
Но писать функцию для каждого сочетания complex и double, как это делалось выше для operator+, невыносимо нудно. Кроме того, близкие к реальности средства комплексной арифметики должны предоставлять по меньшей мере дюжину таких функций. Посмотрите, например, на тип complex, описанный в «complex.h».
6.3.1 Конструкторы
Альтернативу использованию нескольких функций (перегрженных) составляет описание конструктора, который по заданнму double создает complex. Например:
class complex (* // ... complex(double r) (* re=r; im=0; *) *);
Конструктор, требующий только один параметр, необязтельно вызывать явно:
complex z1 = complex(23); complex z2 = 23;
И z1, и z2 будут инициализированы вызовом complex(23).
Конструктор – это предписание, как создавать значение данного типа. Когда требуется значение типа, и когда такое значение может быть создано конструктором, тогда, если такое значение дается для присваивания, вызывается конструктор. Например, класс complex можно было бы описать так:
class complex (* double re, im; public: complex(double r, double i = 0) (* re=r; im=i; *)
friend complex operator+(complex, complex); friend complex operator*(complex, complex); *);
и действия, в которые будут входить переменные complex и целые константы, стали бы допустимы. Целая константа будет интерпретироваться как complex с нулевой мнимой частью. Наример, a=b*2 означает:
a=operator*( b, complex( double(2), double(0) ) )
Определенное пользователем преобразование типа применется неявно только тогда, когда оно является единственным.