String& String::operator+=( const String& rhs ) {

 // ... Реализация ...

 return *this;

}

String operator+( const String& lhs, const String& rhs ) {

 String temp; // изначально пуста

 // выделение достаточного количества памяти

 temp.Reserve(lhs.size() + rhs.size());

 // Конкатенация строк и возврат

 return (temp += lhs) += rhs;

}

Исключения

В некоторых случаях (например, оператор operator*= для комплексных чисел), оператор может изменять левый аргумент настолько существенно, что более выгодным может оказаться реализация оператора operator*= посредством оператора operator*, а не наоборот.

Ссылки

[Alexandrescu03a] • [Cline99] §23.06 • [Meyers96] §22 • [Sutter00] §20

<p>28. Предпочитайте канонический вид ++ и --, и вызов префиксных операторов</p>Резюме

Особенность операторов инкремента и декремента состоит в том, что у них есть префиксная и постфиксная формы с немного отличающейся семантикой. Определяйте операторы operator++ и operator-- так, чтобы они подражали поведению своих встроенных двойников. Если только вам не требуется исходное значение — используйте префиксные версии операторов.

Обсуждение

Старая шутка гласит, что язык называется С++, а не ++С, потому что язык был улучшен (на что указывает инкремент), но многие продолжают использовать его как С (предыдущее значение до инкремента). К счастью, эту шутку можно считать устаревшей, но это отличная иллюстрация для понимания отличия между двумя формами операторов.

В случае ++ и -- постфиксные формы операторов возвращают исходное значение, в то время как префиксные формы возвращают новое значение. Лучше всего реализовывать постфиксный оператор с использованием префиксного. Вот канонический вид такого использования:

// ---- Префиксные операторы -----------------------

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

<p>29. Используйте перегрузку, чтобы избежать неявного преобразования типов</p>Резюме
Перейти на страницу:

Все книги серии C++ In-Depth

Похожие книги