Встроенные( intrinsic ) типы данных — это типы данных, которые компилятор "знает" изначально, такие как int, float, double и другие, а также различные типы указателей. В главах 3, "Выполнение математических операций", и 4, "Выполнение логических операций", были описаны операторы, определённые в С++ для встроенных типов. С++ позволяет программисту определять операторы для создаваемых им классов в дополнение к встроенным операторам. Эта возможность называется перегрузкой операторов.
Обычно перегрузка операторов необязательна и не используется новичками в программировании на С++. Более того, многие опытные программисты вообще считают данную возможность излишней и опасной. Однако есть один оператор, который вы будете просто вынуждены переопределять: это оператор присвоения.
►Сравнение операторов и функций...271
Оператор представляет собой не более чем встроенную функцию с определённым синтаксисом. Так, сложение а+b можно рассматривать, как если бы это была запись operator+( a , b ). С++ даёт каждому оператору имя в стиле функции. Такое функциональное имя оператора состоит из ключевого слова operator, за которым следует символ оператора, а за ним — соответствующие типы аргументов. Например, оператор +, который суммирует целые числа и возвращает целое число, имеет имя int operator+( int , int ).
Любой оператор может быть определён для пользовательского класса. Так, я могу создать Complex operator*( Complex& , Complex& ), который позволит мне умножить два объекта типа Complex. Новый оператор может иметь ту же семантику, что и перегружаемый, но не обязан. При перегрузке операторов действуют следующие правила.
■■■
■ Программист не может перегрузить операторы ., ::, * ( разыменование ) и &.
■ Программист не может вводить новые операторы, например, х$у.
■ Формат оператора не может быть изменён. Например, вы не можете определить оператор %i, поскольку % — бинарный оператор.
■ Приоритет операторов не может быть изменён. Программа не может заставить оператор + выполняться раньше оператора *.
■ Операторы не могут быть переопределены для встроенных типов — вы не в состоянии изменить смысл записи 1+2. Существующие операторы могут быть перегружены только для вновь создаваемых типов.
■■■
Перегрузка операторов — одна из тех вещей, которые выглядят лучше, чем есть на самом деле. По моему опыту, перегрузка операторов создаёт больше проблем, чем решает, с двумя важными исключениями, которые будут рассмотрены далее в этой главе.
►Потоковые операторы...272
Операторы считывания из потока и записи в него, << и >>, — это не что иное, как переопределённые операторы левого и правого сдвига для набора классов, представляющих потоки ввода-вывода. Эти определения находятся в файле iostream. Таким образом, запись cout <<"some string" означает вызов функции operator<<( " some string" ). Наши старые знакомые сin и cout представляют собой предопределённые объекты, связанные с клавиатурой и монитором соответственно. Подробнее мы поговорим об этом в главе 24, "Использование потоков ввода-вывода".
►Мелкое копирование — глубокие проблемы...272
Независимо от того, что думаете вы и многие другие о переопределении операторов, вам всё равно придётся переопределять оператор присвоения для множества ваших классов. С++ предоставляет определение operator=( ) по умолчанию, но этот оператор просто выполняет почленное копирование. Такое присвоение отлично работает для встроенных операторов типа int.
int i ;
i = 10 ;
Точно так же ведёт себя присвоение по умолчанию и для пользовательских классов. В следующем примере каждый член source копируется в соответствующий член destination.
void fn( )
{
MyStruct source , destination ;
destination = source ;
}
Оператор присвоения по умолчанию вполне работоспособен для большинства классов, однако при выделении ресурсов, таких как память из кучи, начинаются проблемы. В этом случае программист должен переопределить оператор operator=( ) для корректной передачи ресурса.