|
Программирование >> Оптимизация возвращаемого значения
избавиться от накладных расходов, связанных с вызовом operator*, объявив функцию как inline: Наиболее эффективный способ реализовать функцию, возвращающую объект. inline const Rational operator*(const Rationals Ihs, const Rationals: rhs) return Rational(Ihs.numerator0 * rhs.numerator(), Ihs.denominator() * rhs.denominator()) ; Да, да , - бормочете вы, - оптимизация, оптимизация. Какая разница, что компиляторы могут сделать? Я хочу знать то, что они делают\ Работает ли эта штука в реальных компиляторах? Работает! Такая специфическая оптимизация - устранить локальный временный объект, используя точку возврата функции (и, возможно, заменяя его объектом в месте вызова функции) - хорошо известна и широко применяется. Она даже имеет название: оптимизация возвращаемого значения. Наличие особого названия для этого типа оптимизации может объяснить, почему она так широко доступна. Программисты, ишушие компилятор С++, могут спросить продавцов, реализуется ли в нем оптимизация возвращаемого значения. Если один продавец отвечает Да , а другой спрашивает: Что-что реализуется? , то первый их них обладает определенным конкурентным преимуществом. Ах, капитализм! Иногда невозможно его не любить! Правило 21. Используйте перегрузку, чтобы избежать неявного преобразования типов Вот код, который выглядит совершенно правильным: class UPInt { Класс для целых чисел public: неограниченной точности. UPIntО ; UPInt(int value); Почему возвращаемое значение равно const, объясняется в правиле 6. const UPInt operator*(const UPIntS Ihs, const UPIntS rhs) ; UPInt upil, upi2; UPInt upi3 = upil + upi2; Здесь нет сюрпризов. И upil, и upi2 являются объектами типа UPInt, поэтому их сложение просто приводит к вызову operator* для UPInt. Рассмотрим теперь следующие операторы: ир13 = upil + 10; upi3 = 10 + upi2; Их выполнение также будет успешным, при этом для преобразования целого числа 10 в тип UPInt будут созданы временные объекты (см. правило 19). Такие преобразования обычно выполняются компиляторами автоматически, но вряд ли вам нравится тратить дополнительные ресурсы компьютера на создание временных объектов. Так же как многие хотят пол5ать кредиты от правительства, не возвращая их, так и большинство программистов С++ желали бы использовать неявные преобразования типов, не неся расходов по созданию временных объектов. Но как это сделать - ведь не существует вычислительного эквивалента покрытия бюджетного дефицита? Если сделать шаг назад, то обнаружится, что цель на самом деле состоит не в преобразовании типов, а в том, чтобы можно было вызывать operator+ с аргументами типов UPInt и int. Для этого подойдет неявное преобразование типов, но не нужно путать цели и средства. Существует другой способ успешного выполнения вызовов operator+ для аргументов разных типов, который устраняет необходимость преобразования типов. Если вам нужно сложить объекты типа UPInt и int, вы можете сделать это, объявив несколько функций с разными наборами типов параметров: const UPInt operator+(const UPInt& Ihs, Сложить UPInt const UPIntSc rhs) ; и UPInt. const UPInt operator+(const UPInt& Ihs, Сложить UPInt int rhs); и int. const UPInt operator+(int Ihs, Сложить int const UPInt& rhs) и UPInt. UPInt upil, upi2; UPInt upi3 = upil + upi2; Bee в порядке, временные объекты для upil или upi2 не создаются. upi3 = upil + 10; Все в порядке, временные объекты для upil или 10 не создаются. upi3 = 10 + upi2; Все в порядке, временные объекты для 10 или upi2 не создаются. После выполнения перегрузки, что позволяет избежать преобразования типов, главное не увлечься и не объявить еще и функции типа: const UPInt operator+(int Ihs, int rhs); Ошибка! Хотя определенная логика в этом построении есть: перегрузить все возможные комбинации operator+ для типов UPInt и int. После того как заданы три вышеприведенные функции, остается только operator+, оба аргумента которого имеют тип int, поэтому-то и возникает соблазн добавить его. Но в языке С++ существуют свои правила игры, и согласно им один из аргументов любого перегруженного оператора должен иметь определенный пользователем тип. Тип int является встроенным, поэтому нельзя перегружать оператор, задавая только аргументы этого типа. (Если бы разрешалось изменять значение встроенных операций, это привело бы к хаосу. К примеру, попытка перегрузки operator+ в данном случае изменила бы значение сложения двух целых чисел.) Перегрузка функций для того, чтобы избежать создания временных объектов, касается не только функций операторов. Например, во многих программах вам может потребоваться сделать допустимым использование объекта string везде, где есть char*, и наоборот. Аналогично, если вы включаете в код класс чисел (допустим, класс комплексных чисел complex - см. правило 35), вам может понадобиться, чтобы в любом месте вместо численного объекта такого типа разрешалось подставить число типа int или double. Этого нетрудно достичь, перегрузив все функции с аргументами типа string, char*, complex и избежав тем самым преобразований типов. Кроме того, важно не забывать о соотношении 80-20 (см. правило 16). Нет смысла реализовать множество перегруженных функций, если это не приведет к заметному ул5шению суммарной эффективности использующих их программ. Правило 22. По возможности применяйте оператор присваивания вместо отдельного оператора Большинство программистов ожидает, что если можно написать х = х + у; Х = Х- у; то можно написать и X += у; X -= у; Но если X и у имеют определенный пользователем тип, то нет гарантии, что это действительно так. В языке С++ не предусмотрено связи между operator*, operator= и operator+=, поэтому если вы хотите, чтобы все три оператора существовали и привычно соотносились, вам придется реализовать такое поведение самостоятельно. То же самое относится к операторам -, *, / и т.д. Хороший способ гарантировать естественное соотношение между оператором присваивания (например, operator+=) и соответствующим отдельным оператором (таким как operator*) - реализовать второй оператор через первый (см. также правило 6). Сделать это достаточно легко: class Rational { public: Rationals operator+=(const Rationals rhs) ; Rationals operator-=(const Rationals rhs); };
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |