3

我已经用 C++ 编程了一段时间,但肯定不会称自己为专家。这个问题不是为了解决我遇到的实际问题,而是更多地了解 C++ 在做什么。

想象一下,我有一个需要单个参数的函数:

void doSomething(SomeClass& ref)
{
    // do something interesting
}

(注意:参数是对 SomeClass 的引用)然后我这样调用函数:

int main(int argc, char *argv[])
{
    SomeClass a;
    doSomething(a);
}

为什么这是合法的 C++?该函数期望引用 SomeClass,但我将其传递给 SomeClass 类型的静态分配变量。引用就像指针不是吗?如果我们用指针替换引用,编译器会抱怨。为什么引用以这种方式与指针不同,幕后发生了什么?

对不起,如果这是一个愚蠢的问题,这只是困扰我!

4

8 回答 8

2

如果您不再将引用视为类似于指针,我认为您会更好地理解这一点。我想说进行这种比较的原因有两个:

  1. 引用允许您将对象传递给函数以允许对其进行修改。这是 C 中指针的一个流行用例。
  2. 指针和引用的实现通常在编译后几乎相同。

但它们不同的东西。您可以将引用视为为对象赋予新名称的一种方式。int& x = y;表示我想为我目前称为y. 这个新名字是x。这两个标识xy,现在都指同一个对象。

这就是您将对象本身作为参考传递的原因。你是说你希望函数有自己的标识符来引用你传递的对象。如果您不将 & 符号放在参数列表中,则该对象将被复制到函数中。这种复制通常是不必要的。

于 2011-04-16T22:27:23.550 回答
1

您没有将“SomeClass 类型的静态分配变量”传递给它,而是将其传递给SomeClass您在main().

你的功能

void doSomething(SomeClass& ref)

导致对ain main 的引用被传入。这就是&在参数列表中的类型之后所做的。

如果省略&, 则将调用SomeClass's ( 's) 复制构造函数,并且您的函数将在;中获取对象a的新本地副本。在这种情况下,您在函数中所做的任何事情都不会被看到amain()refmain()

于 2011-04-16T22:21:05.553 回答
1

您的代码不正确 -是返回实例SomeClass a();的函数的前向声明- 这样的声明在函数范围内无效。aSomeClass

假设您的意思是SomeClass a;

在大多数实际情况下,引用与指针非常相似——主要区别在于您不能合法地拥有对 NULL 的引用,而您可以拥有指向 NULL 的指针。正如您所注意到的,指针和引用的语法是不同的——您不能在需要引用的地方传递指针。

如果您将引用视为“不能为空且不能指向其他地方的指针”,那么您几乎已经涵盖了。您正在传递一些引用您的本地a实例的东西 - 如果doSomething修改它的参数,那么它实际上是直接修改您的 local a

于 2011-04-16T22:27:57.850 回答
1
SomeClass a();

这是一个函数签名,而不是一个对象。

它应该是

SomeClass a; // a is an object

那么你的代码是有效的。

为什么这是合法的 C++?

(假设您修复了前一点)C++ 标准说,如果您的函数属性是一个引用,那么您应该提供一个具有名称(左值)的对象。所以在这里,这是合法的。如果它是一个 const 引用,您甚至可以提供一个临时的(一个 r 值,没有名称)。

该函数期望引用 SomeClass,但我将其传递给 SomeClass 类型的静态分配变量。

它期望引用 SomeClass 的非常量实例,这就是您提供的。该实例不是静态的,它只是在堆栈上分配。对象的分配与操作它的方式无关,只有作用域才可以。分配对象的方式(像这里一样在堆栈上,或者通过使用 new/delete 在堆上)只告诉对象的生命周期。只要不是 const,即使是静态对象也可以在您的函数中传递。

我认为您在这里混合了一些语言概念...

引用就像指针不是吗?

不。

引用是对象的“昵称”。不多也不少。

好的,实际上它是作为具有特殊规则的指针实现的,但并非在每次使用中都是如此:编译器可以自由地以它想要的任何方式实现它。在函数属性的情况下,它通常被实现为指针。但你甚至不必知道它。

对你来说,它只是一个对象的昵称。

如果我们用指针替换引用,编译器会抱怨。为什么引用以这种方式与指针不同,幕后发生了什么?

我猜您的第一个错误确实使您感到困惑?

于 2011-04-16T22:29:35.697 回答
1

也许您的困惑源于以下事实:如果您有两个功能:

void doThingA(int a) {
    a=23;
}
void doThingB(int &a) {
    a=23;
} 

对它们的调用看起来相同,但实际上非常不同:

int a=10;
doThingA(a);
doThingB(a);

第一种情况,doThingA(int),创建一个值为 10 的全新变量,将 23 赋给它并返回。调用者中的原始变量保持不变。在第二种情况下,doThingB(int&),其中变量通过引用传递,创建一个新变量,其地址与传入的变量相同。这就是人们说通过引用传递就像通过指针传递时的意思。因为当 doThingB(int&) 改变传入的值时,两个变量共享相同的地址(占用内存位置),所以调用者中的变量也发生了变化。

我喜欢把它看作是传递指针,没有那些烦人的指针语法。尽管如此,我发现修改通过引用传递的变量的函数令人困惑,而且我几乎从不这样做。我要么通过 const 引用

void doThingB(const int &a);

或者,如果我想修改值,显式传递一个指针。

void doThingB(int *a);
于 2011-04-16T23:45:18.527 回答
0

虽然这个问题已经得到了充分的回答,但我还是忍不住多说几句关于“C++ 中的引用”的相关语言特性。

作为 C 程序员,在将变量传递给函数时,我们有两种选择:

  1. 传递变量的值(创建一个新副本)
  2. 将指针传递给变量。

谈到 C++,我们通常是在处理对象。由于空间(以及速度)方面的考虑,不建议在需要处理该对象的每个函数调用上复制此类对象。传递变量地址(通过指针方法)有很多好处,虽然我们可以将指针设为“const”以避免通过指针进行任何更改,但指针的语法相当笨拙(在某个地方错过了取消引用运算符或两个并最终花费数小时调试!)。

C++,在提供“参考”时,封装了这两个选项中最好的:

  1. 引用可以理解为和传递地址一样好
  2. 使用引用的语法与处理变量本身的语法相同。
  3. 引用总是指向“某物”。因此没有“空指针”异常。

此外,如果我们引用“const”,我们不允许对原始变量进行任何更改。

于 2013-01-12T23:27:43.647 回答
0

引用不是指针。您只需将参数作为“按值”传递并使用它。在引擎盖下只会使用一个指针,但这只是在引擎盖下。

于 2011-04-16T22:24:16.890 回答
0

引用根本不像指针,它是其他对象的别名 - 一个新名称。这是两者兼得的原因之一!

考虑一下:

Someclass a;

Someclass& b = a;
Someclass& c = a;

这里我们首先创建一个对象a,然后我们说bc是同一对象的其他名称。没有新的东西被创建,只有两个额外的名字。

borc是函数的参数时,别名 fora在函数内部可用,您可以使用它来引用实际对象。

就是这么简单!使用指针时,您不必使用 、 或 like 跳过任何&循环*->

于 2011-04-17T06:17:49.807 回答