如何在C++中使用参考参数?

Sag*_*tic 51 c++ reference-parameters

我试图了解如何使用参考参数.我的文本中有几个例子,但是它们太复杂了我无法理解为什么以及如何使用它们.

你想如何以及为什么要使用参考?如果您没有将参数作为参考,而是将其&关闭,会发生什么?

例如,这些功能之间的区别是什么:

int doSomething(int& a, int& b);
int doSomething(int a, int b);
Run Code Online (Sandbox Code Playgroud)

我知道引用变量用于更改形式 - >引用,然后允许参数的双向交换.然而,这是我的知识范围,更具体的例子会有很大帮助.

GMa*_*ckG 115

将引用视为别名.当您在引用上调用某些内容时,您实际上是在引用所引用的对象上调用它.

int i;
int& j = i; // j is an alias to i

j = 5; // same as i = 5
Run Code Online (Sandbox Code Playgroud)

在功能方面,请考虑:

void foo(int i)
{
    i = 5;
}
Run Code Online (Sandbox Code Playgroud)

上面int i是一个值,传递的参数是按值传递.这意味着如果我们说:

int x = 2;
foo(x);
Run Code Online (Sandbox Code Playgroud)

i将是一个复制x.因此设置i为5对此没有影响x,因为它是x被更改的副本.但是,如果我们i提供参考:

void foo(int& i) // i is an alias for a variable
{
    i = 5;
}
Run Code Online (Sandbox Code Playgroud)

然后说foo(x)不再复制x; i x.因此,如果我们说foo(x),在函数内部i = 5;完全相同x = 5;,并且x更改.

希望这有点澄清.


为什么这很重要?编程时,您永远不想复制和粘贴代码.你想创建一个完成一项任务的功能,并且它做得很好.只要需要执行该任务,就可以使用该功能.

所以我们假设我们要交换两个变量.看起来像这样:

int x, y;

// swap:
int temp = x; // store the value of x
x = y;        // make x equal to y
y = temp;     // make y equal to the old value of x
Run Code Online (Sandbox Code Playgroud)

好,太棒了.我们想让它成为一个函数,因为:swap(x, y);更容易阅读.那么,让我们试试这个:

void swap(int x, int y)
{
    int temp = x;
    x = y;
    y = temp;
}
Run Code Online (Sandbox Code Playgroud)

这不行!问题是这是交换两个变量的副本.那是:

int a, b;
swap(a, b); // hm, x and y are copies of a and b...a and b remain unchanged
Run Code Online (Sandbox Code Playgroud)

在C中,不存在引用,解决方案是传递这些变量的地址; 也就是说,使用指针*:

void swap(int* x, int* y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}

int a, b;
swap(&a, &b);
Run Code Online (Sandbox Code Playgroud)

这很好用.但是,它使用起来有点笨拙,实际上有点不安全.swap(nullptr, nullptr),交换两个nothings和dereferences null指针...未定义的行为!可通过一些检查修复:

void swap(int* x, int* y)
{
    if (x == nullptr || y == nullptr)
        return; // one is null; this is a meaningless operation

    int temp = *x;
    *x = *y;
    *y = temp;
}
Run Code Online (Sandbox Code Playgroud)

但看起来我们的代码有多笨拙.C++引入了引用来解决这个问题.如果我们可以为变量添加别名,我们将获得我们正在寻找的代码:

void swap(int& x, int& y)
{
    int temp = x;
    x = y;
    y = temp;
}

int a, b;
swap(a, b); // inside, x and y are really a and b
Run Code Online (Sandbox Code Playgroud)

既易于使用又安全.(我们不能意外地传入null,没有空引用.)这是有效的,因为函数内部发生的交换实际上发生在函数外的别名变量上.

(注意,永远不要写一个swap函数.:)标题中已经存在一个<algorithm>,并且模板适用于任何类型.)


另一个用途是删除调用函数时发生的副本.考虑一下我们的数据类型非常大.复制此对象需要花费大量时间,我们希望避免这种情况:

struct big_data
{ char data[9999999]; }; // big!

void do_something(big_data data);

big_data d;
do_something(d); // ouch, making a copy of all that data :<
Run Code Online (Sandbox Code Playgroud)

但是,我们真正需要的只是变量的别名,所以让我们指出.(同样,回到C,我们将传递我们的大数据类型的地址,解决复制问题但引入笨拙.):

void do_something(big_data& data);

big_data d;
do_something(d); // no copies at all! data aliases d within the function
Run Code Online (Sandbox Code Playgroud)

这就是为什么你会听到它说你应该一直通过引用传递东西,除非它们是原始类型.(因为内部传递别名可能是用指针完成的,就像在C中一样.对于小对象,制作副本的速度更快,然后担心指针.)

请记住,你应该是const-correct.这意味着如果您的函数没有修改参数,请将其标记为const.如果do_something上面只看了但没有改变data,我们将其标记为const:

void do_something(const big_data& data); // alias a big_data, and don't change it
Run Code Online (Sandbox Code Playgroud)

我们避免复制,我们说"嘿,我们不会修改它." 这有其他副作用(像临时变量这样的东西),但你现在不应该担心.

相反,我们的swap功能不能const,因为我们确实在修改别名.

希望这能澄清更多.


*粗略指针教程:

指针是保存另一个变量地址的变量.例如:

int i; // normal int

int* p; // points to an integer (is not an integer!)
p = &i; // &i means "address of i". p is pointing to i

*p = 2; // *p means "dereference p". that is, this goes to the int
        // pointed to by p (i), and sets it to 2.
Run Code Online (Sandbox Code Playgroud)

所以,如果你已经看过指针版本交换函数,我们传递我们想要交换的变量的地址,然后我们进行交换,取消引用以获取和设置值.

  • +1 - 很好的参考介绍.只想添加一些关于别名的清晰度.考虑别名的好方法是将其视为昵称.例如,让我们说简有一个昵称 - 吉尔.你可以说简或吉尔,但仍指同一个人.所以在上一个带引用的例子中,你可能会说`i`是`x`的昵称 - 听起来不一样,但两者都指的是同一个变量.也就是说,它们看起来不同,但它们指的是内存中的相同位置. (3认同)