代码之家  ›  专栏  ›  技术社区  ›  casualcoder

为什么最好写func(const Class&value)?

  •  4
  • casualcoder  · 技术社区  · 17 年前

    func( const Class &value ) func( Class value ) 毫无疑问,现代编译器将使用这两种语法中的任何一种来做最有效的事情。这仍然是必要的,还是只是非优化编译器时代的延续?

    similar question

    9 回复  |  直到 9 年前
        1
  •  17
  •   JaredPar    17 年前

    这两个签名之间有两个很大的语义差异。

    首先是使用 & 在类型名称中。这表示该值是通过引用传递的。删除此项会导致对象按值传递,这基本上将对象的副本传递给函数(通过复制构造函数)。对于只需要读取数据的操作(通常用于 const & )对对象进行完整复制会产生不必要的开销。对于不小或是集合的类,这种开销并非微不足道。

    二是使用 const 这可以防止函数通过以下方式意外修改值的内容 value 参考。它允许调用者在一定程度上保证值不会被函数改变。是的,在许多情况下,传递副本会让呼叫者对此有更深入的保证。

        2
  •  8
  •   John Kugelman Michael Hodel    17 年前

    第一种形式不创建对象的副本,它只是传递一个指向现有副本的引用(指针)。第二种形式创建副本,这可能很昂贵。这不是优化掉的东西:拥有对象的副本和拥有原始对象之间存在语义差异,复制需要调用类的复制构造函数。

    void foo(double bar) 而不是 void foo(const double &var) 但为了避免对代码进行微观优化,一般来说,你应该通过引用传递所有real deal对象,只传递内置类型,比如 int 和 void * 按价值。

        3
  •  8
  •   Ðаn    17 年前

    有一个 还没有人提到的区别:对象切片。在某些情况下,您可能 需要 const& (或 & )以获得正确的行为。

    考虑另一个类 Derived 其继承自 Class 在客户端代码中,您创建了一个实例 衍生的 你传递给谁 func() 如果你有 func(const Class&) func(Class) 新 (临时)实例 (不是 衍生的 )in func .

    如果满足以下条件,这种行为差异(而非性能差异)可能很重要 函数 这反过来又让人沮丧。比较运行以下代码的结果:

    #include <typeinfo.h>
    
    struct Class
    {
        virtual void Foo() {};
    };
    class Derived : public Class {};
    
    void f(const Class& value)
    {
        printf("f()\n");
    
        try
        {
            const Derived& d = dynamic_cast<const Derived&>(value);
            printf("dynamic_cast<>\n");
        }
        catch (std::bad_cast)
        {
            fprintf(stderr, "bad_cast\n");
        }
    }
    
    void g(Class value)
    {
        printf("g()\n");
    
        try
        {
            const Derived& d = dynamic_cast<const Derived&>(value);
            printf("dynamic_cast<>\n");
        }
        catch (std::bad_cast)
        {
            fprintf(stderr, "bad_cast\n");
        }
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        Derived d;
        f(d);
        g(d);
        return 0;
    }
    
        4
  •  4
  •   LiraNuna    17 年前

    当然,现代编译器会做到这一点 使用其中一种最有效的方法

    编译器不会编译你的“意思”,它会编译你的 告诉它 编译器只适用于低级优化和程序员忽略的问题(如for循环内的计算、死代码等)。

    在第二个例子中,你告诉编译器要做的是复制类——它会不假思索地这样做——即使你没有使用它,这也是你要求编译器做的。

    Class &value 可以写信给(有时是需要的)。

        5
  •  4
  •   Greg Hewgill    17 年前

    以下是一些参数声明之间的差异:

                               copied  out  modifiable
    func(Class value)           Y       N    Y
    func(const Class value)     Y       N    N
    func(Class &value)          N       Y    Y
    func(const Class &value)    N       N    N
    

    哪里:

    • copy:调用函数时,会复制输入参数
    • out:value是一个“out”参数,这意味着在func()中进行的修改在函数返回后将在函数外部可见
    • 可修改:可以在func()中修改值

    那么,两者之间的差异 func(Class value) 和 func(const Class &value) 是:

    • 第一个方法复制输入参数(通过调用Class复制构造函数),并允许func()中的代码进行修改 value
    • 不 复制一份,然后 不 允许func()中的代码进行修改 价值
        6
  •  1
  •   Amber    17 年前

    如果你使用前者,然后尝试改变 value

    如果你使用后者,然后尝试改变 价值

    因此,前者更容易发现错误。

        7
  •  1
  •   dicroce    17 年前

        8
  •  1
  •   R Samuel Klatchko    17 年前

    优化编译器无法为您处理此问题的原因是单独编译的问题。在C++中,当编译器为调用者生成代码时,它可能无法访问函数本身的代码。据我所知,最常见的调用约定通常是调用者调用复制构造函数,这意味着如果不需要,函数本身的编译就不可能阻止复制构造函数。

        9
  •  0
  •   iain    17 年前

    最好按值传递参数的唯一一次是在无论如何都要复制参数的时候。

    std::string toUpper( const std::string &value ) {
        std::string retVal(value);
        transform(retVal.begin(), retVal.end(), charToUpper());
        return retVal;
    }
    

    std::string toUpper( std::string value ) {
        transform(value.begin(), value.end(), charToUpper());
        return value;
    }
    

    在这种情况下,如果值参数是常规对象,则第二个示例的速度与第一个示例相同,但如果值参数为R值,则速度更快。

    Want Speed? Pass by Value. 我无法给出更好的解释。