【问题标题】:Difference between const reference and normal parameterconst 引用和普通参数的区别
【发布时间】:2011-02-07 07:15:54
【问题描述】:
void DoWork(int n);
void DoWork(const int &n);

有什么区别?

【问题讨论】:

    标签: c++ parameters reference constants


    【解决方案1】:

    第一个方法通过值传递n,即将n的副本发送到函数。第二个通过引用传递n,这基本上意味着将指向调用函数的n 的指针发送给函数。

    对于像int 这样的整数类型,作为常量引用传递没有多大意义,因为引用的大小通常与引用(指针)的大小相同。在复制成本很高的情况下,通常最好通过 const 引用传递。

    【讨论】:

    • 尺寸不是唯一的问题。通过 const 引用传递允许对原始对象的更改在函数内部可见。按值传递将函数体与此类更改隔离开来。
    • @Charles 当然,这在某些情况下确实可能是有益的(而在另一些情况下是有害的;))。
    • 您可以在多线程环境中观察到在通过值与通过引用传递像 int 这样的类型时的不同行为。
    【解决方案2】:

    当你传递一个大的结构/类时,差异会更加突出:

    struct MyData {
        int a,b,c,d,e,f,g,h;
        long array[1234];
    };
    void DoWork(MyData md);
    void DoWork(const MyData& md);
    

    当您使用“普通”参数时,您按值传递参数,从而创建您传递的参数的副本。如果您使用的是 const 引用,则通过引用传递它,并且不会复制原始数据。

    在这两种情况下,都不能从函数内部修改原始数据。


    编辑:
    在某些情况下,原始数据可能会被修改,正如Charles Bailey 在他的answer 中指出的那样。

    【讨论】:

    • 通过 ref 是否需要const?我的意思是,您的答案是否不适用于我们仅将“正常”参数与 ref 参数进行比较的情况?
    • 通过 ref 传递当然不需要const.. 只是有了const,编译器将帮助您避免修改原始数据.. 但这可以被故意绕过正如查尔斯·贝利所指出的那样……
    【解决方案3】:

     void DoWork(int n);
    

    n是实参值的副本,在函数内改变n的值是合法的。与

    void DoWork(const int &n);
    

    n是对实参的引用,改变其值是不合法的。

    【讨论】:

      【解决方案4】:

      因为你们都没有提到 const 关键字...

      const 关键字修改类型声明的类型或函数参数的类型,防止值变化。 (来源:MS)

      换句话说:通过引用传递参数会将其暴露给被调用者修改。使用 const 关键字可防止修改。

      【讨论】:

        【解决方案5】:

        重要的区别在于,当通过const 引用时,不会创建新对象。在函数体中,参数实际上是传入对象的别名。

        因为引用是const 引用,所以函数体不能直接更改该对象的值。这与按值传递具有相似的属性,其中函数体也无法更改传入对象的值,在这种情况下,因为参数是副本。

        存在重要的差异。如果参数是const 引用,但传递给它的对象实际上不是const,那么对象的值可能会在函数调用本身期间更改。

        例如

        int a;
        
        void DoWork(const int &n)
        {
            a = n * 2;  // If n was a reference to a, n will have been doubled 
        
            f();  // Might change the value of whatever n refers to 
        }
        
        int main()
        {
            DoWork(a);
        }
        

        此外,如果传入的对象实际上不是 const,那么该函数可以(即使不建议这样做)通过强制转换来更改其值。

        例如

        void DoWork(const int &n)
        {
            const_cast<int&>(n) = 22;
        }
        

        如果传入的对象实际上是const,这将导致未定义的行为。

        当参数通过 const 引用传递时,额外的成本包括取消引用、更差的对象局部性、更少的编译优化机会。

        当参数通过值传递时,额外的成本是需要创建参数副本。通常,这仅在对象类型很大时才需要考虑。

        【讨论】:

        • 您能否解释一下当您在回答中提到更糟糕的对象位置时会受到什么惩罚?
        • 可能是en.wikipedia.org/wiki/Locality_of_reference。 TLDR 当内存的常用部分组合在一起时,由于缓存内部结构,您可以获得更好的性能。影响这一点的另一种方法是使用 GCC 属性 hotcold
        • 为什么通过 const 引用传递会导致更糟糕的对象局部性
        【解决方案6】:

        首先,没有 cv 限定引用的概念。因此,术语“const 引用”是不正确的,通常用于描述“对 const 的引用”。最好开始谈论它的含义。

        $8.3.2/1- "Cv 限定的引用格式不正确,除非 通过使用 typedef (7.1.3) 或 模板类型参数 (14.3),在这种情况下 cv-qualifiers 被忽略。”

        这里有区别

        $13.1 - “只有最外层的 const 和 volatile 类型说明符 以这种方式忽略参数类型规范的级别; 隐藏在参数类型中的 const 和 volatile 类型说明符 规范是重要的,可以用来区分 重载函数声明.112)。特别是,对于任何类型 T, “指向 T 的指针”、“指向 const T 的指针”和“指向 volatile T 的指针”是 被视为不同的参数类型,如“对 T 的引用”, “引用 const T”和“引用 volatile T”。

        void f(int &n){
           cout << 1; 
           n++;
        }
        
        void f(int const &n){
           cout << 2;
           //n++; // Error!, Non modifiable lvalue
        }
        
        int main(){
           int x = 2;
        
           f(x);        // Calls overload 1, after the call x is 3
           f(2);        // Calls overload 2
           f(2.2);      // Calls overload 2, a temporary of double is created $8.5/3
        }
        

        【讨论】:

          【解决方案7】:

          你可以在函数中传递值的三种方法

          1. 按值传递

            void f(int n){
                n = n + 10;
            }
            
            int main(){
                int x = 3;
                f(x);
                cout << x << endl;
            }
            

            输出:3.缺点:当参数x通过f函数时,编译器会在x的内存中创建一个副本。太浪费内存了。

          2. 通过引用传递

            void f(int& n){
                n = n + 10;
            }
            
            int main(){
                int x = 3;
                f(x);
                cout << x << endl;
            }
            

            输出:13。它消除了按值传递的缺点,但如果程序员不想更改值,则使用常量引用

          3. 常量引用

            void f(const int& n){
                n = n + 10; // Error: assignment of read-only reference  ‘n’
            }
            
            int main(){
                int x = 3;
                f(x);
                cout << x << endl;
            }
            

            输出:在n = n + 10 处抛出错误,因为当我们传递 const 引用参数参数时,它是只读参数,您无法更改 n 的值。

          【讨论】:

          • 你的意思是在调用 f() 之后在 main() 中有 print 语句吗?因为 f() 里面的 print 语句,我认为第一个例子的输出是错误的:应该是 13,不是吗?
          • 不,@Seb 你不正确。输出将是 3 本身。这就是按值传递的意思。 x 被传递,即 3 而不是 x 本身。您所说的是通过引用传递,即第二个示例...
          • 嗯。是的,现在看来是对的。我的评论是 3 年前的事了。也许它被编辑了? (如果没有,对不起。(:)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-11
          • 2016-07-15
          • 2023-03-22
          相关资源
          最近更新 更多