Когда я должен использовать временные переменные?
В частности, мне интересно, какой из них я должен написать:
{
shared_ptr<GuiContextMenu> subMenu = items[j].subMenu.lock();
if (subMenu)
subMenu->setVisible(false);
}
или же:
{
if (items[j].subMenu.lock())
items[j].subMenu.lock()->setVisible(false);
}
Я не обязан следовать каким-либо руководствам по стилю. После оптимизации я не думаю, что какой-либо выбор влияет на производительность. Какой стиль обычно предпочитается и почему?
РЕДАКТИРОВАТЬ: тип элементов [J]. SubMenu является Boost:: Strong_ptr. lock() создает из него shared_ptr. На самом деле в двух версиях выше имеется неоднозначное различие в отношении того, как долго длится временный shared_ptr, поэтому я обернул два моих примера в {фигурные скобки}, чтобы устранить там неоднозначность.
10 ответов
Альтернативный метод:
if(shared_ptr<GuiContextMenu> subMenu = items[j].subMenu.lock()) {
subMenu->setVisible(false);
}
//subMenu is no longer in scope
Я предполагаю, что subMenu
это weak_ptr
, в этом случае ваш второй метод создает два временных, которые могут быть или не быть проблемой. И ваш первый метод добавляет переменную в более широкую область, чем нужно. Лично я стараюсь избегать назначений в if
заявления, но это один из немногих случаев, когда я чувствую его более полезным, чем альтернативы.
В этом конкретном случае вам действительно следует использовать версию с временной переменной. Причина не в производительности, а в правильности - в принципе, вам не гарантируется, что два x.lock()
вызовы возвращают то же значение (например, если другой поток освобождает последнюю сильную ссылку на объект между двумя вызовами). Удерживая сильную ссылку во временной переменной, вы гарантируете, что она не исчезнет.
Кроме этого:
компиляторы обычно не могут оптимизировать вызовы функций, если только они не являются доказуемо свободными от побочных эффектов (это трудно сделать, но атрибуты могут помочь) или встроенными. В этом случае вызов имеет побочные эффекты.
использование временных файлов может привести к более коротким, более читаемым и более обслуживаемым программам (например, в случае ошибки вы исправляете это в одном месте)
Я думаю, что вы правы в том, что оба варианта после оптимизации ничем не отличаются.
Лично я объявил бы новую переменную, если она делает код более читабельным, например, когда вы объединяете вызовы или помещаете вызовы функций внутри вызовов функций. Пока он поддерживается и код достигает того же эффекта без разницы в скорости, все сводится к читаемому коду.
Редактировать:
mmyers купил хороший комментарий. Да, будь осторожен с звонком lock()
дважды, а не один раз. Они будут иметь разные эффекты в зависимости от вашей реализации.
Когда возвращаемое значение является чем-то иным, чем логическое значение, назначение его промежуточной переменной часто может упростить отладку. Например, если вы перешагните через следующее:
if( fn() > 0 ) ...
все, что вы узнаете после факта, это то, что функция вернула значение меньше нуля, нуля или больше. Даже если возвращаемое значение было неверным, код все равно может работать. Присвоение его переменной, которую можно проверить в вашем отладчике, позволит вам определить, ожидалось ли возвращаемое значение.
Когда возвращаемое значение является логическим, фактическое значение полностью неявно выражено потоком кода, поэтому оно менее критично; однако, при ведении кода вы позже можете обнаружить, что вам нужен этот результат, поэтому вы можете в любом случае принять решение сделать его привычкой.
Даже там, где возвращаемое значение является логическим, еще одна проблема, требующая рассмотрения, заключается в том, имеет ли функция требуемые побочные эффекты и может ли на это повлиять оценка короткого замыкания. Например, в заявлении:
if( isValid && fn() ) ...
функция никогда не будет вызываться isValid является ложным.
Обстоятельств, при которых код может быть взломан при обслуживании неосторожным программистом (и зачастую менее опытные программисты получают задачи по обслуживанию), много, и, вероятно, лучше избегать этого.
Выбор, по сути, остается за вами, но основной вещью, на которую вы должны обратить внимание, является ремонтопригодность.
В этом конкретном примере, я думаю, это зависит от того, что lock()
делает. Функция дорогая? Может ли он возвращать разные вещи каждый раз, когда вызывается функция (может ли он вернуть указатель в первый раз и NULL во второй раз)? Работает ли другой поток, который может чередоваться между двумя вызовами lock()
?
Для этого примера вам нужно понять поведение lock()
и остальная часть вашего кода, чтобы принять разумное решение.
Что вы предпочитаете. Для меня это зависит от того, насколько я буду его использовать; для двух строк я мог бы просто выписать это оба раза, тогда как я создаю переменную, если я использую это больше. Однако вам, скорее всего, придется поддерживать этот код и продолжать его искать, поэтому используйте все, что вам подходит. Конечно, если вы работаете в компании с правилами кодирования, следуйте им.
Я предпочитаю первый в большинстве случаев, потому что он делает код более понятным и легким для чтения, а значит, менее подвержен ошибкам. Например, вы забыли круглые скобки в этом втором примере:) В этом случае, на самом деле, я бы, вероятно, сделал то же, что вы сделали во втором примере, однако, если бы мне нужно было использовать это подменю несколько раз, я бы пошел с первый, чтобы сделать код проще для чтения. Что касается производительности, я думаю, что любой здравомыслящий компилятор сможет оптимизировать это (возможно, поэтому вы не видели никакой разницы в производительности).
Кроме того, как указал mmyers, это также зависит от того, что делает lock(). В общем, если это простой метод получения или что-то в этом роде, все будет в порядке.
Я думаю, что предпочтительным стилем является любой стиль, который, по вашему мнению, делает ваш код более читабельным и обслуживаемым. Если вы являетесь командой из более чем одного человека, единственное другое соображение заключается в том, что, как правило, для всех будет хорошей идеей использовать один и тот же стиль, опять же для удобства чтения и простоты обслуживания.
В этом случае я думаю, что вы должны использовать временный. Даже если вы знаете, что реализация.lock() недорогая, это может измениться. Если вам не нужно дважды вызывать lock (), не делайте этого. Значение здесь в том, что он отделяет ваш код от реализации lock(). И это вообще хорошо.