По привычке при доступе к значениям через. или ->, я назначаю их переменным каждый раз, когда значение будет использоваться более одного раза. Насколько я понимаю, для языков сценариев, таких как ActionScript, это очень важно. Однако в C / C ++ мне интересно, является ли это бессмысленной рутиной; трачу ли я усилия, которые компилятор сделает за меня, или я практикуюсь в хорошей практике, и почему?

 public struct Foo
    {
        public:
        Foo(int val){m_intVal = val;)
        int GetInt(){return m_intVal;}

        int m_intVal; // not private for sake of last example
    };
    public void Bar()
    {
        Foo* foo = GetFooFromSomewhere();
        SomeFuncUsingIntValA(foo->GetInt()); // accessing via dereference then function
        SomeFuncUsingIntValB(foo->GetInt()); // accessing via dereference then function
        SomeFuncUsingIntValC(foo->GetInt()); // accessing via dereference then function

        // Is this better?
        int val = foo->GetInt();
        SomeFuncUsingIntValA(val);
        SomeFuncUsingIntValB(val);
        SomeFuncUsingIntValC(val);

        ///////////////////////////////////////////////
        // And likewise with . operator
        Foo fooDot(5);
        SomeFuncUsingIntValA(fooDot.GetInt()); // accessing via function
        SomeFuncUsingIntValB(fooDot.GetInt()); // accessing via function
        SomeFuncUsingIntValC(fooDot.GetInt()); // accessing via function

        // Is this better?
        int valDot = foo.GetInt();
        SomeFuncUsingIntValA(valDot);
        SomeFuncUsingIntValB(valDot);
        SomeFuncUsingIntValC(valDot);

        ///////////////////////////////////////////////
        // And lastly, a dot operator to a member, not a function
        SomeFuncUsingIntValA(fooDot.m_intVal); // accessing via member
        SomeFuncUsingIntValB(fooDot.m_intVal); // accessing via member
        SomeFuncUsingIntValC(fooDot.m_intVal); // accessing via member

        // Is this better?
        int valAsMember = foo.m_intVal;
        SomeFuncUsingIntValA(valAsMember);
        SomeFuncUsingIntValB(valAsMember);
        SomeFuncUsingIntValC(valAsMember);
    }
3
Jt Meili 30 Авг 2014 в 23:12
4
Вы работаете над программой, в которой 100 мс определяют разницу между непригодным для использования приложением и работающим? Если нет, то ваш вопрос бессмыслен. Если да, вам нужно профилировать, что снова делает ваш вопрос спорным. Не знаю, правильно ли я себя понял, но есть так много вопросов о преждевременных микрооптимизациях, что у меня нет желания распространяться.
 – 
bolov
30 Авг 2014 в 23:24
Думаю, я нашел завещание.
 – 
bolov
30 Авг 2014 в 23:49
Объекты в C++ не являются хеш-таблицами, как в ActionScript и тому подобном. Компилятор просто размещает объект в памяти так, как вы его укажете. Таким образом, доступ к полю так же прост, как смещение указателя, а затем его отслеживание — никаких операций с хэш-таблицей не требуется.
 – 
tmyklebu
31 Авг 2014 в 10:51

3 ответа

Лучший ответ

Хорошо, поэтому я пытаюсь найти ответ здесь.

Краткая версия: вам это точно не нужно.

Длинная версия: вам может понадобиться это сделать.

Итак, вот оно: в интерпретируемых программах, подобных Javascript, подобные вещи могут иметь заметное влияние. В скомпилированных программах, таких как C ++, не так уж и много, чтобы вообще не было.

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

Есть случаи, когда компилятор не может этого сделать. Это когда невозможно доказать, что несколько вызовов дают одинаковый результат. Тогда ему ничего не остается, кроме как делать все звонки.

Теперь давайте предположим, что компилятор делает неправильный выбор, и вы в качестве меры предосторожности предпринимаете усилия по микрооптимизации. Вы производите оптимизацию и увеличиваете производительность на 10% (что уже является чрезмерно оптимистичным показателем для такого рода оптимизации) в этой части кода. Но что вы знаете, ваш код тратит только 1% своего времени на эту часть кода. Остальное время, скорее всего, вы потратите на несколько горячих циклов и ожидание выборки данных. Таким образом, вы тратите немалые усилия на оптимизацию кода только для того, чтобы получить прирост производительности на 0,1% за общее время, что даже не будет наблюдаться из-за внешних факторов, которые изменяют время выполнения намного больше, чем это количество. .

Так что не тратьте время на микрооптимизации в C ++.

Однако бывают случаи, когда вам может потребоваться это и даже более безумные вещи. Но это только после правильного профилирования вашего кода, и это уже другое обсуждение.

Так что беспокойтесь о удобочитаемости, не беспокойтесь о микрооптимизациях.

4
bolov 30 Авг 2014 в 23:55
Пожалуйста :)
 – 
bolov
30 Авг 2014 в 23:59

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

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

Если вы снова и снова будете использовать одно и то же выражение для генерации этого значения, этот факт станет гораздо менее очевидным. Во-первых, с первого взгляда сложно сказать, действительно ли выражения идентичны (особенно если они длинные). Во-вторых, не очевидно, дают ли последовательные вычисления одного и того же выражения одинаковые результаты.

Наконец, разделение длинных выражений на более мелкие с помощью промежуточных переменных может значительно упростить отладку кода в пошаговом отладчике, поскольку это дает пользователю гораздо большую степень контроля с помощью команд «шаг за шагом» и «шаг за шагом».

3
AnT stands with Russia 30 Авг 2014 в 23:52

Конечно, лучше иметь такую ​​временную переменную с точки зрения удобочитаемости и ремонтопригодности.

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

1
Adam Stelmaszczyk 30 Авг 2014 в 23:37