What is the difference between a const reference and normal parameter?

void DoWork(int n);
void DoWork(const int &n);

What's the difference?

188459 次浏览

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

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

当您传递一个大型结构/类时,这种差异更加明显:

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

When you use use 'normal' parameter, you pass the parameter by value and hence creating a copy of the parameter you pass. If you are using const reference, you pass it by reference and the original data is not copied.

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


编辑:
在某些情况下,原始数据可能会得到修改,正如 查尔斯 · 贝利在他的 回答中指出的那样。

With

 void DoWork(int n);

n is a copy of the value of the actual parameter, and it is legal to change the value of n within the function. With

void DoWork(const int &n);

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

既然你们都没提到 const 关键字..。

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

换句话说: 通过引用传递参数将使参数暴露给被调用方进行修改。使用 康斯特关键字可以防止修改。

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

因为引用是 const引用,函数体不能直接更改该对象的值。这与通过值传递类似,函数体也不能更改传入的对象的值,在本例中是因为参数是一个副本。

两者之间存在着关键的区别。如果参数是 const引用,但传递给它的对象实际上不是 const,那么在函数调用本身期间对象的值可能会发生变化。

E.g.

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;
}

This would cause undefined behaviour if the object passed in was actually const.

当参数通过常量引用传递时,额外的开销包括解引用、对象局部性更差、编译优化的机会更少。

当参数通过值传递时,需要创建一个参数副本。通常,只有在对象类型较大时才需要考虑这个问题。

Firstly, there is no concept of cv-qualified references. So the terminology 'const reference' is not correct and is usually used to describle 'reference to const'. It is better to start talking about what is meant.

$8.3.2/1-“ Cv 限定的参考资料格式不正确,除非 cv-qualifiers are introduced through the use of a typedef (7.1.3) or of a template type argument (14.3), in which case the cv-qualifiers 都被忽略了”

Here are the differences

$13.1 - "Only the const and volatile type-specifiers at the outermost 以这种方式忽略参数类型规范的级别; 常量和易失性类型说明符隐藏在参数类型中 规格是有意义的,可以用来区分 重载的函数声明。112)特别是,对于任何类型 T, “指向 T 的指针”、“指向常量 T 的指针”和“指向易失性 T 的指针”是 被认为是不同的参数类型,如“引用 T”, “引用常量 T”和“引用挥发性 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
}

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

  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 的值。