Правила программирования на С и С++. Главы 7-8
Страница 58. Глава 8.Е. Перегрузка операций


 

Глава 8.Е. Перегрузка операций

145. Операция - это сокращение (без сюрпризов).

Операция - это не произвольный значок, означающий все, что вы ни пожелаете. Это аббревиатура англоязычного слова. Например, символ + значит "прибавить", поэтому вы не должны заставлять перегруженный operator+() делать что-нибудь еще. Хотя здесь все ясно (вы можете определить a + b для вычитания b из a, но не должны делать этого), я на самом деле веду речь о более проблемах более творческого характера.

Вы можете благоразумно доказывать, что, когда выполняете конкатенацию, то "прибавляете" одну строку к концу другой, поэтому перегрузка + для конкатенации может быть приемлема. Вы также можете доказывать, что разумно использовать операции сравнения для лексикографического упорядочивания в классе string, поэтому перегрузка операций ?, == и т.д. также вероятно пойдет. Вы не сможете аргументированно доказать, что - или * имеют какой-нибудь смысл по отношению к строкам.

Другим хорошим примером того, как нельзя действовать, является интерфейс С++ iostream. Использование сдвига (??) для обозначения "вывод" является нелепым. Ваши функции вывода в С назывались printf(), а не shiftf(). Я понимаю, что Страуструп выбрал сдвиг, потому что он сходен с механизмом перенаправления ввода/вывода различных оболочек UNIX, но этот довод на самом деле не выдерживает проверки. Страуструп исходил из того, что все программисты на С++ понимают перенаправление в стиле UNIX, но эта концепция отсутствует в некоторых операционных системах - например, в Microsoft Windows. К тому же, для того, чтобы аналогия была полной, операция > должна быть перегружена для выполнения операции затирания, а >> - добавления в конец. Тем не менее, тот факт, что > и >> имеют различный приоритет, делает реализацию такого поведения затруднительной. Дело осложняется тем, что операторы сдвига имеют неправильный уровень приоритета. Оператор типа cout ?? x += 1 не будет работать так, как вы ожидаете, потому что у ?? более высокий приоритет, чем у +=, поэтому оператор интерпретируется как (cout ?? x) += 1, что неверно. С++ нуждается в расширяемости, обеспечиваемой системой iostream, но он вынужден добиваться ее за счет введения операторов "ввода" и "вывода", имеющих низший приоритет по отношению к любому оператору языка.

Аналогия проблеме "сдвиг как вывод" может быть найдена в проектировании компьютерных систем. Большинство проектировщиков аппаратуры были бы счастливы использовать + вместо OR, а * вместо AND, потому что такая запись используется во многих системах проектирования электронных компонентов. Несмотря на это, перегрузка операции operator+() в качестве OR явно не нужна в С++. К тому же, лексема ?? означает "сдвиг" в С и С++; она не означает "вывод".

Как завершающий пример этой проблемы - я иногда видел реализации класса "множество", определяющие | и ? со значениями "объединение" и "пересечение". Это может иметь смысл для математика, знакомого с таким стилем записи, но при этом не является выражением ни С, ни С++, поэтому будет незнакомо для вашего среднего программиста на С++ (и вследствие этого с трудом сопровождаться). Амперсанд является сокращением для AND; вы не должны назначать ему произвольное значение. Нет абсолютно ничего плохого в a.Union(b) или a.intersect(b). (Вы не можете использовать a.union(b) со строчной буквой u, потому что union является ключевым словом).

 
« Предыдущая статья   Следующая статья »