Выбрать главу

■■■

■ Программист не может перегрузить операторы ., ::, * ( разыменование ) и &.

■ Программист не может вводить новые операторы, например, х$у.

■ Формат оператора не может быть изменён. Например, вы не можете определить оператор %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=( ) для корректной передачи ресурса.

Оператор присвоения очень похож на конструктор копирования, а при использовании они практически идентичны.

    void fn( MyClass& mc )

    {

        MyClass newMC( mc ) ; /* Здесь используется конструктор копирования */

        MyClass newerMC = mc ; /* Менее очевидно, что здесь также используется конструктор копирования */

        MyClass newestMC ; /* Создание объекта по умолчанию */

        newestMC = mc ; /* Присвоение */

    }

_________________

272 стр. Часть 5. Полезные особенности

Создание newMC следует стандартному шаблону создания нового объекта как зеркального отображения существующего с использованием копирующего конструктора MyClass( MyClass& ). Несколько менее очевидно, что объект newerMC также создаётся при помощи конструктора копирования. Запись MyClass а = b — всего лишь другой способ записи MyClass a( b ). То, что в первом варианте записи имеется символ "=", не приводит к вызову оператора присвоения. Однако в случае с объектом newestMC всё не совсем так. Сначала этот объект создаётся с использованием конструктора по умолчанию, а затем перезаписывается объектом mc с помощью оператора присвоения.

Подобно конструктору копирования, оператор присвоения должен быть переопределён, если мелкое копирование приводит к некорректным результатам ( см. материал, представленный в главе 18, "Копирующий конструктор" ). Простейшее правило: если у класса есть пользовательский конструктор копирования, то переопределите для него и оператор присвоения.