>
可以重新分配指针:
int x = 5;
int y = 6;
int *p;
p = &x;
p = &y;
*p = 10;
assert(x == 5);
assert(y == 10);
引用不能,而且必须在初始化时赋值:
int x = 5;
int y = 6;
int &r = x;
指针在堆栈上有自己的内存地址和大小(在x86上为4字节),而引用共享相同的内存地址(与原始变量),但也会占用堆栈上的一些空间。 由于引用与原始变量本身具有相同的地址,因此将引用视为同一变量的另一个名称是安全的。 注意:指针指向的东西可以在堆栈或堆上。 同上,参考资料。 我在这条语句中的主张并不是指指针必须指向堆栈。 指针只是一个保存内存地址的变量。 此变量在堆栈上。 因为引用在堆栈上有自己的空间,而且地址与它引用的变量相同。 更多关于堆栈与堆的信息。 这意味着存在编译器不会告诉您的引用的真实地址。
int x = 0;
int &r = x;
int *p = &x;
int *p2 = &r;
assert(p == p2);
您可以有指向指针的指针指向提供额外间接级别的指针。 而引用只提供一个间接级别。
int x = 0;
int y = 0;
int *p = &x;
int *q = &y;
int **pp = &p;
pp = &q;//*pp = q
**pp = 4;
assert(y == 4);
assert(x == 0);
指针可以直接分配nullptr
,而引用则不能。 如果您足够努力地尝试,并且您知道如何操作,您可以使引用的地址nullptr
。 同样,如果您足够努力,您可以有一个对指针的引用,然后该引用可以包含nullptr
。
int *p = nullptr;
int &r = nullptr; <--- compiling error
int &r = *p; <--- likely no compiling error, especially if the nullptr is hidden behind a function call, yet it refers to a non-existent int at address 0
指针可以在数组上迭代; 您可以使用++
转到指针指向的下一项,使用+4
转到第5个元素。 无论指针指向的对象大小如何,都是如此。
指针需要用*
取消引用才能访问它所指向的内存位置,而引用则可以直接使用。 类/结构的指针使用->
访问其成员,而引用使用.
。
引用不能填充到数组中,而指针可以(由用户@litb提到)
常量引用可以绑定到临时引用。 指针不能(没有一些间接):
const int &x = int(12); //legal C++
int *y = &int(12); //illegal to dereference a temporary.
这使得const&
在参数列表中使用时更加安全。
引用可以被认为是一个常量指针(不要与指向常量值的指针混淆!) 使用自动间接,即编译器将为您应用*
运算符。
必须使用非Null值初始化所有引用,否则编译将失败。 既不可能获得引用的地址--地址运算符将返回被引用值的地址--也不可能对引用进行算术。
C程序员可能不喜欢C++引用,因为当间接发生时,或者如果参数通过值或指针传递而不查看函数签名时,它将不再明显。
C++程序员可能不喜欢使用指针,因为它们被认为是不安全的--尽管引用实际上并不比常量指针安全,除非是在最琐碎的情况下--缺乏自动间接的便利性,并且带有不同的语义内涵。
考虑C++常见问题中的以下语句:
即使引用在底层汇编语言中经常使用地址来实现,但请不要将引用看作是指向对象的一个看起来滑稽的指针。 引用就是对象。 它不是指向对象的指针,也不是对象的副本。 它就是对象。
但是如果一个引用真的是对象,怎么会有悬而未决的引用呢? 在非托管语言中,引用不可能比指针更“安全”--通常没有一种方法可以跨作用域边界可靠地别名值!
来自C语言的背景,C++引用可能看起来是一个有点愚蠢的概念,但在可能的情况下,还是应该使用它们而不是指针:自动间接很方便,并且引用在处理RAII时变得特别有用--但不是因为任何明显的安全优势,而是因为它们使编写惯用代码变得不那么笨拙。
RAII是C++的核心概念之一,但它与复制语义的交互并不平凡。 通过引用传递对象避免了这些问题,因为不涉及复制。 如果语言中没有引用,则必须使用指针,因为指针使用起来比较麻烦,从而违反了语言设计原则,即最佳实践解决方案应该比备选方案更容易。
如果你想真正学究一点,有一件事你可以用引用做,而用指针做不到:延长临时对象的生存期。 在C++中,如果将常量引用绑定到临时对象,则该对象的生存期将成为引用的生存期。
std::string s1 = "123";
std::string s2 = "456";
std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;
在本例中,s3_copy复制作为连接结果的临时对象。 而s3_reference实质上成为临时对象。 它实际上是对一个临时对象的引用,该对象现在具有与引用相同的生存期。
如果您尝试在没有常量
的情况下执行此操作,它应该无法编译。 您不能将非常量引用绑定到临时对象,也不能为此获取其地址。