了解多个 .和 ->运算符使用?

Understanding cost of multiple . and -> operator use?

本文关键字:运算符 gt 了解      更新时间:2023-10-16

当通过访问值时,出于习惯。或者->,每当值要被多次使用时,我都会将它们分配给变量。我的理解是,在像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);
    }

好的,所以我试着在这里找到答案。

简言之:你绝对不需要这么做。

长版本:您可能需要这样做。

因此,在像Javascript这样的解释程序中,这类事情可能会产生明显的影响。在编译过的程序中,比如C++,并没有到完全不编译的地步。

大多数时候,你不需要担心这些事情,因为编译器优化算法(和实际实现)已经投入了大量资源,编译器将正确地决定该做什么:分配一个额外的寄存器并保存结果,以便重用它,或者每次重新计算并保存寄存器空间,等等

有些情况下编译器无法做到这一点。也就是说,它不能证明多个调用产生相同的结果。然后它别无选择,只能打所有的电话。

现在让我们假设编译器做出了错误的选择,作为预防措施,您需要进行微观优化。您进行了优化,并在这部分代码上实现了10%的性能提升(对于这种优化来说,这已经是一个过于乐观的数字)。但是,你知道吗,你的代码只在这部分代码中花费了1%的时间。剩下的时间很可能花在一些热循环中,等待数据提取。因此,您花费了不可忽略的精力来优化代码,但总时间的性能提高了0.1%,这甚至是不可观察的,因为外部因素会使执行时间的变化远远超过这个数量。

因此,不要花时间在C++中进行微观优化。

然而,在某些情况下,你可能需要这样做,甚至更疯狂的事情。但这只是在对代码进行适当的分析之后,这是另一个讨论。

所以,要担心可读性,不要担心微观优化。

这个问题实际上与->.运算符无关,而是与一般的重复表达式有关。的确,大多数现代编译器都足够聪明,可以优化重复计算同一表达式的代码(假设它没有明显的副作用)。

然而,使用显式中间变量通常会使程序可读性更强,因为它显式地暴露了在所有上下文中都应该使用相同值的事实。它暴露了这样一个事实,即您的意图在所有上下文中使用相同的值。

如果您一次又一次地重复使用相同的表达式来生成该值,那么这个事实就不那么明显了。首先,第一眼很难判断这些表达是否真的相同(尤其是当它们很长的时候)。其次,对看似相同的表达进行顺序评估是否会产生相同的结果并不明显。

最后,通过使用中间变量将长表达式分割成更小的表达式,可以在逐步调试器中简单地调试代码,因为它通过"逐步执行"answers"逐步结束"命令为用户提供了更大程度的控制。

在可读性和可维护性方面,有这样的临时变量肯定会更好。

就性能而言,您不必担心现阶段的这种微观优化(过早优化)。此外,现代C++编译器无论如何都可以优化它,所以你真的不必担心它