指针和引用参数之间的区别?


69

这些是一样的吗:

int foo(bar* p) {
  return p->someInt();
}

int foo(bar& r) {
  return r.someInt();
}

忽略空指针电位。这两个函数在功能上是否相同,无论someInt()是虚拟的还是传递给bar的子类bar

这个切片什么吗?

bar& ref = *ptr_to_bar;

Answers:


71

有意在标准中未指定使用指针实现C ++引用。引用更像是变量的“同义词”,而不是指向变量的指针。当有可能意识到指针在某些情况下会显得过大时,这种语义为编译器带来了一些可能的优化。

还有一些区别:

  • 您不能将NULL分配给引用。这是一个关键的区别,也是您偏爱另一个的主要原因。
  • 当您获取指针的地址时,您将获取指针变量的地址。当您获取引用的地址时,您将获取所引用变量的地址。
  • 您无法重新分配参考。一旦初始化,它将在整个生命中指向同一个对象。

6
实际上,如果将引用转换为该类型的指针,然后取消引用该点,则可以将NULL分配给引用,例如:int&ref = (int)NULL; 如果将参考放置在联合中并在联合中更改相应的值,也可以重新分配参考。
格兰特·彼得斯2009年

而且我应该补充一点,这些绝对不应该做,因为当一个函数要求一个引用时,它永远不会期望为NULL,尽管大多数引用的实现方式基本上与指针相同,但并非在所有情况下都是如此。平台。
格兰特·彼得斯2009年

2
如果您要分配一个并集的相应值,那么您不是真的在分配一个引用吗?
shoosh

阐明将非常量引用分配给const值(类似于NULL,因为它是const,宏定义的值)不是“可能的”,这很有帮助。尽管在技术上可以通过低级操作来进行此类操作,但不应在生产代码中执行此操作。C ++ 11有助于引入nullptr作为一种类型,并鼓励其被普遍采用。
扎克·史密斯

15

忽略可以用一个而不是另一个完成的所有语法和可能性,以及在其他答案(针对其他问题)中解释的指针和引用之间的区别...是的,这两个函数在功能上完全相同!两者都调用该函数,并且均能很好地处理虚函数。

不,您的线不会切开。它只是将引用直接绑定到指针所指向的对象。

有关为什么要在另一个上使用的一些问题:

我没有亲自提出分歧,而是委托您参加那些您想知道的事情。


13

引用是一个常量指针,即,您不能将引用更改为引用其他对象。如果更改,则引用对象的值也会更改。

例如:

       int j = 10;
       int &i = j;
       int l = 20;
       i = l; // Now value of j = 20

       int *k = &j;
       k = &l;   // Value of j is still 10

6

是的,它们在功能上是相同的。由于引用将要求您在使用它之前将其设置为对象,因此您将不必处理空指针或指向无效内存的指针。

看到语义上的差异也很重要:

  • 当您实际将对象传递给普通对象时,请使用引用-但它太大了,因此将引用传递给对象而不是进行复制(如果您不修改实际对象)更有意义。
  • 当您要处理内存地址而不是对象时,请使用指针。

5

我已经很长时间没有使用C ++了,所以我什至不会尝试真正回答您的问题(对不起);但是,Eric Lippert刚刚发表了一篇关于指针/引用的出色文章,我认为我会向您指出。


4

不知道是否有人回答了您的第二个问题,该问题隐藏在底部,关于切片……不,这不会导致切片。

切片是将派生的对象分配(复制)到基类对象时-派生的类的专业化被“切开”。请注意,我说的是对象被复制,我们不是在谈论要复制/分配的指针,而是对象本身。

在您的示例中,这没有发生。您只是取消引用指向Bar对象的指针(从而导致Bar对象),该指针在引用初始化中用作右值。不确定我是否正确使用了术语...


3

正如其他所有人所提到的,在实现中,引用和指针在很大程度上是相同的。有一些小警告:

  • 您不能将NULL分配给引用(shoosh提到了这一点):这很重要,因为没有“未定义”或“无效”的引用值。

  • 您可以将临时变量作为const引用传递,但是将指针传递给临时变量是不合法的。

例如,这可以:

class Thingy; // assume a constructor Thingy(int,int)
void foo(const Thingy &a)
{ 
   a.DoSomething();
}

void bar( ) 
{
  foo( Thingy(1,2) );
}

但是大多数编译器会抱怨

void foo2( Thingy * a);

void bar2()
{
  foo( &Thingy(1,2) );
}
  • 获取变量的地址以获取指针将强制编译器将其保存到内存。将引用分配给局部变量只会创建一个同义词。在某些情况下,这可能使编译器将数据保留在寄存器中,并避免了load-hit-store。但是,这仅适用于局部变量-一旦通过引用将某些内容作为参数传递,就无法避免将其保存到堆栈中。

 

void foo()
{
   int a = 5;
   // this may be slightly more efficient
   int &b = a;
   printf( "%d", ++b );
   // than this
   int *c = &a;
   printf( "%d", ++(*c) );
}
  • 同样,__ restrict关键字不能应用于引用,只能应用于指针。

  • 您不能对引用进行指针算术运算,因此,如果您有一个指向数组的指针,则数组中的下一个元素可以通过p + 1拥有,而引用只能在其整个生命中指向一件事。


1

这些功能显然不是“相同的”,但就虚拟行为而言,它们的行为将相似。关于切片,仅当您处理值而不是引用或指针时,才会发生这种情况。


1
实际上,它们可能会生成相同的机器代码,因此从某种意义上讲它们是相同的。
jmucchiello
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.