// ---- Префиксные операторы -----------------------
T& T::operator++() { // Префиксный вид:
// выполнение // - Выполнение
// инкремента // действий
return *this; // - return *this;
}
T& T::operator--() { // Префиксный вид:
// Выполнение // - Выполнение
// декремента // действий
return *this; // - return *this;
}
// ---- Постфиксные операторы ---------------------
T T::operator++(int) { // Постфиксный вид:
T old(*this); // - Запоминаем старое значение
++*this; // - Вызов префиксной версии
return old; // - Возврат старого значения
}
T T::operator--(int) { // Постфиксный вид:
T old(*this); // - Запоминаем старое значение
--*this; // - Вызов префиксной версии
return old; // - Возврат старого значения
}
В вызывающем коде лучше использовать префиксные операторы, если только вам не требуется исходное значение, возвращаемое постфиксной версией. Префиксная форма семантически эквивалентна, она вводится практически так же, и зачастую немного эффективнее, так как создает на один объект меньше. Это не преждевременная оптимизация, а устранение преждевременной пессимизации (см. рекомендацию 9).
Исключения
Шаблоны, используемые для научных вычислений (например, шаблоны для представления тензоров или матриц), к которым предъявляются жесткие требования по производительности. Для таких шаблонов часто приходится искать более эффективные способы реализации префиксных форм операторов.
Ссылки
[Cline99] §23.07-08 • [Dewhurst03] §87 • [Meyers96] §6 • [Stroustrup00] §19.3 • [Sutter00] §6, §20
29. Используйте перегрузку, чтобы избежать неявного преобразования типов
Резюме
Не приумножайте объекты сверх необходимости (Бритва Оккама): неявное преобразование типов обеспечивает определенное синтаксическое удобство (однако см. рекомендацию 40), но в ситуации, когда допустима оптимизация (см. рекомендацию 8) и желательно избежать создания излишних объектов, можно обеспечить перегруженные функции с сигнатурами, точно соответствующими распространенным типам аргументов, и тем самым избежать преобразования типов.
Обсуждение
Когда вы работаете в офисе и у вас заканчивается бумага, что вы делаете? Правильно — вы идете к копировальному аппарату и делаете несколько копий чистого листа бумаги.
Как бы глупо это ни звучало, но зачастую это именно то, что делает неявное преобразование типов: создание излишних временных объектов только для того, чтобы выполнить некоторые тривиальные операции над ними и тут же их выбросить (см. рекомендацию 40). В качестве примера можно рассмотреть сравнение строк:
class String { // ...
String(const char* text); // Обеспечивает неявное
// преобразование типов
};
bool operator==(const String&, const String&);
// ... где-то в коде ...
if (someString == "Hello"){ ... }
Ознакомившись с приведенными выше определениями, компилятор компилирует приведенное сравнение таким образом, как если бы оно было записано в виде someString == String("Hellо")
. Это слишком расточительно — копировать символы, чтобы потом просто прочесть их. Решение этой проблемы очень простое — определить перегруженные функции, чтобы избежать преобразования типов, например:
bool operator==(const String& lhs, const string& rhs); // #1
bool operator==(const String& lhs, const char* rhs); // #2
bool operator==(const char* lhs, const String& rhs); // #3
Это выглядит как дублирование кода, но на самом деле это всего лишь "дублирование сигнатур", поскольку все три варианта обычно используют одну и ту же функцию. Вряд ли вы впадете в ересь преждевременной оптимизации (см. рекомендацию 8) при такой простой перегрузке, тем более что этот метод слабо применим при проектировании библиотек, когда трудно заранее предсказать, какие именно типы будут использоваться в коде, критическом по отношению к производительности.
Ссылки
[Meyers96] §21 • [Stroustrup00] §11.4, §C.6 • [Sutter00] §6
30. Избегайте перегрузки &&
, ||
и ,
(запятой)
Резюме
Мудрость — это знание того, когда надо воздержаться. Встроенные операторы &&
, ||
и ,
(запятая) трактуются компилятором специальным образом. После перегрузки они становятся обычными функциями с весьма отличной семантикой (при этом вы нарушаете рекомендации 26 и 31), а это прямой путь к трудноопределимым ошибкам и ненадежности. Не перегружайте эти операторы без крайней необходимости и глубокого понимания.
Читать дальше