指针变量和引用变量之间的区别是什么?


当前回答

除了这里的所有答案,

可以使用引用实现运算符重载:

my_point operator+(const my_point& a, const my_point& b)
{
  return { a.x + b.x, a.y + b.y };
}

使用参数作为值将创建原始参数的临时副本,而使用指针将不会调用此函数,因为指针算法。

其他回答

为了避免混淆,我想输入一些输入,我确信这主要取决于编译器如何实现引用,但在gcc的情况下,引用只能指向堆栈上的变量的想法实际上并不正确,例如:

#include <iostream>
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout << str_ref << std::endl;
    // It works! Now lets print and compare actual memory addresses
    std::cout << str_ptr << " : " << &str_ref << std::endl;
    // Exactly the same, now remember to free the memory on the heap
    delete str_ptr;
}

其输出如下:

THIS IS A STRING
0xbb2070 : 0xbb2070

如果您注意到甚至内存地址都完全相同,这意味着引用成功地指向了堆上的一个变量!现在,如果你真的想变得古怪,这也很有效:

int main(int argc, char** argv) {
    // In the actual new declaration let immediately de-reference and assign it to the reference
    std::string &str_ref = *(new std::string("THIS IS A STRING"));
    // Once again, it works! (at least in gcc)
    std::cout << str_ref;
    // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
    delete &str_ref;
    /*And, it works, because we are taking the memory address that the reference is
    storing, and deleting it, which is all a pointer is doing, just we have to specify
    the address with '&' whereas a pointer does that implicitly, this is sort of like
    calling delete &(*str_ptr); (which also compiles and runs fine).*/
}

其输出如下:

THIS IS A STRING

因此,引用是引擎盖下的指针,它们都只是存储一个内存地址,地址指向的位置是不相关的,如果我调用std::cout<<str_ref;调用delete str_ref后?很明显,它编译得很好,但在运行时会导致分段错误,因为它不再指向有效变量,我们本质上有一个中断的引用仍然存在(直到它超出范围),但没有用。

换句话说,引用只是一个指针,它抽象了指针机制,使其更安全、更容易使用(没有意外的指针数学,没有混淆“.”和“->”等),假设您没有像上面的例子那样尝试任何废话;)

现在,不管编译器如何处理引用,它总是有某种指针,因为引用必须引用特定内存地址处的特定变量,才能按预期工作,因此无法绕过这一点(因此称为“引用”)。

对于引用,唯一需要记住的重要规则是必须在声明时定义它们(头中的引用除外,在这种情况下,必须在构造函数中定义引用,在构造包含引用的对象之后,再定义它就太晚了)。

请记住,我上面的例子只是说明引用是什么的例子,你永远不想以这些方式使用引用!为了正确使用参考文献,这里已经有很多答案,这些答案一针见血

简言之

指针:指针是保存另一个变量的内存地址的变量。指针需要使用*运算符解引用以访问它所指向的内存位置。-摘自Geeks for Geeks

引用:引用变量是别名,即现有变量的另一个名称。引用(如指针)也通过存储对象的地址来实现。-摘自极客对极客

另一张图片了解更多详情:

如果你不熟悉以抽象的甚至学术的方式学习计算机语言,那么语义上的差异可能会显得深奥难懂。

在最高级别上,引用的概念是它们是透明的“别名”。你的计算机可能会使用一个地址来使它们工作,但你不必担心:你应该将它们视为现有对象的“另一个名称”,语法反映了这一点。它们比指针更严格,因此编译器可以在您将要创建悬挂引用时比在您将创建悬挂指针时更可靠地警告您。

除此之外,指针和引用之间当然还有一些实际差异。使用它们的语法明显不同,您不能“重新定位”引用、引用虚无或引用指针。

你忘记了最重要的部分:

带指针的成员访问使用->使用引用的成员访问。

foo.bar明显优于foo->bar,就像vi明显优于Emacs一样:-)

引用的另一个有趣用法是提供用户定义类型的默认参数:

class UDT
{
public:
   UDT() : val_d(33) {};
   UDT(int val) : val_d(val) {};
   virtual ~UDT() {};
private:
   int val_d;
};

class UDT_Derived : public UDT
{
public:
   UDT_Derived() : UDT() {};
   virtual ~UDT_Derived() {};
};

class Behavior
{
public:
   Behavior(
      const UDT &udt = UDT()
   )  {};
};

int main()
{
   Behavior b; // take default

   UDT u(88);
   Behavior c(u);

   UDT_Derived ud;
   Behavior d(ud);

   return 1;
}

默认风格使用引用的“bind const reference to a temporary”方面。