С++ для начинающих


Разработка перегруженных операторов


Операторы присваивания, взятия адреса и оператор “запятая” имеют предопределенный смысл, если операндами являются объекты типа класса. Но их можно и перегружать. Семантика всех остальных операторов, когда они применяются к таким операндам, должна быть явно задана разработчиком. Выбор предоставляемых операторов зависит от ожидаемого использования класса.

Начинать следует с определения его открытого интерфейса. Набор открытых функций-членов формируется с учетом операций, которые класс должен предоставлять пользователям. Затем принимается решение, какие функции стоит реализовать в виде перегруженных операторов.

После определения открытого интерфейса класса проверьте, есть ли логическое соответствие между операциями и операторами:

  • isEmpty() становится оператором “ЛОГИЧЕСКОЕ НЕ”, operator!().
  • isEqual() становится оператором равенства, operator==().
  • copy() становится оператором присваивания, operator=().
  • У каждого оператора есть некоторая естественная семантика. Так, бинарный + всегда ассоциируется со сложением, а его отображение на аналогичную операцию с классом может оказаться удобной и краткой нотацией. Например, для матричного типа сложение двух матриц является вполне подходящим расширением бинарного плюса.

    Примером неправильного использования перегрузки операторов является определение operator+() как операции вычитания, что бессмысленно: не согласующаяся с интуицией семантика опасна.

    Такой оператор одинаково хорошо поддерживает несколько различных интерпретаций. Безупречно четкое и обоснованное объяснение того, что делает operator+(), вряд ли устроит пользователей класса String, полагающих, что он служит для конкатенации строк. Если семантика перегруженного оператора неочевидна, то лучше его не предоставлять.

    Эквивалентность семантики составного оператора и соответствующей последовательности простых операторов для встроенных типов (например, эквивалентность оператора +, за которым следует =, и составного оператора +=) должна быть явно поддержана и для класса. Предположим, для String определены как operator+(), так и operator=() для поддержки операций конкатенации и почленного копирования:


    String s1( "C" );

    String s2( "++" );

    s1 = s1 + s2;    // s1 == "C++"



    Но этого недостаточно

    для поддержки составного оператора присваивания

    s1 += s2;

    Его следует определить явно, так, чтобы он поддерживал ожидаемую семантику.

    Упражнение 15.1

    Почему при выполнении следующего сравнения не вызывается перегруженный оператор operator==(const String&, const String&):

    "cobble" == "stone"

    Упражнение 15.2

    Напишите перегруженные операторы неравенства, которые могут быть использованы в таких сравнениях:

    String != String

    String != С-строка

    C-строка != String

    Объясните, почему вы решили реализовать один или несколько операторов.

    Упражнение 15.3

    Выявите те функции-члены класса Screen, реализованного в главе 13 (разделы 13.3, 13.4 и 13.6), которые можно перегружать.

    Упражнение 15.4

    Объясните, почему перегруженные операторы ввода и вывода, определенные для класса String из раздела 3.15, объявлены как глобальные функции, а не функции-члены.

    Упражнение 15.5

    Реализуйте перегруженные операторы ввода и вывода для класса Screen из главы 13.


    Содержание раздела