为什么c ++引用被认为比指针更安全?

时间:2011-01-17 17:00:11

标签: c++ pointers reference

当c ++编译器为引用和指针生成非常相似的汇编代码时,为什么使用引用首选(并且认为比指针更安全)?

我确实看到了

修改-1:

我正在查看g ++为这个小程序生成的汇编程序代码:

int main(int argc, char* argv[])
{
  int a;
  int &ra = a;
  int *pa = &a;
}

9 个答案:

答案 0 :(得分:18)

它被认为更安全,因为很多人“听到”它更安全,然后告诉其他人,他们现在也“听到”它更安全。

没有一个理解参考文献的人会告诉你他们比指针更安全,他们有相同的缺陷和潜在的无效。

e.g。

#include <vector>

int main(void)
{
    std::vector<int> v;
    v.resize(1);

    int& r = v[0];
    r = 5; // ok, reference is valid

    v.resize(1000);
    r = 6; // BOOM!;

    return 0;
}

编辑:由于关于引用是对象的别名还是绑定到内存位置似乎存在一些混淆,这里是标准中的段落(草案3225,第[basic.life]部分),其中明确指出引用绑定到存储,并且可以比创建引用时存在的对象更长:

  

如果在对象的生命周期结束之后以及对象占用的存储之前被重用或者   释放后,在原始对象占用的存储位置创建一个新对象,指针即可   指向原始对象,引用原始对象或原始对象的名称   对象将自动引用新对象,并且一旦新对象的生命周期开始,就可以   用于操纵新对象,如果:

     
      
  • 新对象的存储空间正好覆盖原始对象占用的存储位置,   和
  •   
  • 新对象与原始对象的类型相同(忽略顶级cv-quali firs),
  •   
  • 原始对象的类型不是const-quali fi ed的,如果是类类型,则不包含任何非静态类型   类型为const-quali fi ed或引用类型的数据成员,
  •   
  • 原始对象是类型为T的派生程度最高的对象,新对象是类型为T的派生程度最高的对象(即,它们不是基类子对象)。
  •   

答案 1 :(得分:7)

这取决于你如何定义“更安全”。

编译器不允许您创建未初始化的引用,也不允许您创建指向NULL的引用,并且在您使用它时,它不会让您意外地将引用引用到其他地方。这些更严格的规则也意味着您的编译器可以为您提供有关常见错误的更多警告,而使用指针则无法确定您是否意味着执行您所执行的操作。

另一方面,语法的透明度 - 即Alexandre C.在 call-site 中提到的将参数作为参考传递的内容 - 提及它很容易没有意识到你正在传递参考。因此,您可能没有意识到您应该维护参数的所有权和生命周期,或者您的参数可能会被永久修改。

答案 2 :(得分:6)

因为引用(它只是其他变量的别名)根据定义不能是NULL,因此提供了一个固有的安全层。

答案 3 :(得分:2)

引用始终从现有对象初始化,因此它永远不能为NULL,而指针变量则允许为NULL。

编辑:感谢所有回复。是的,引用确实可以指向垃圾,我忘了悬挂引用。

答案 4 :(得分:2)

它有点安全,但不是一回事。请注意,与“悬空指针”一样,您有“悬挂引用”的相同问题。例如,从范围对象返回引用会产生未定义的行为,与指针完全相同:

int& f() { int x = 2; return x; }

唯一的好处是您不能创建空引用。即使你努力:

int& null_ref = *((int*)0); // Dereferencing a null pointer is undefined in C++
                            // The variable null_ref has an undefined state.

作为类成员,指针是首选,因为它们具有更好的赋值语义:初始化时无法重新分配引用。如果类中有引用成员,则编译器将无法提供默认赋值运算符。

因此,C ++无法摆脱指针,你可以自由地使用它们:通过将参数作为指针而不是(非常量)引用传递,你可以在调用站点清楚地表明对象将被修改。这可以增加一些安全性,因为您可以通过肉眼看到哪些函数需要修改对象。

我扮演一个魔鬼的拥护者,但引用很容易被滥用。

答案 5 :(得分:2)

因为必须始终初始化引用,并且因为它们必须引用现有对象,所以最终使用悬空引用要比使用未初始化/悬空指针更难(但绝不是不可能)。此外,操作引用更容易,因为您不必担心获取地址并取消引用它们。

但只是为了向您展示引用本身并不能使您的程序100%安全,请考虑以下事项:

int *p = NULL;
int &r = *p;
r = 10; /* bad things will happen here */

或者这个:

int &foo() {
  int i;
  return i;
}

...

int &r = foo();
r = 10; /* again, bad things will happen here */

答案 6 :(得分:0)

你指出答案的答案是肯定的。从“更安全”的角度来看,我认为基本上很难编写如下代码:

int* i;
// ...
cout << *i << endl; // segfault

始终初始化参考,

MyObject* po = new MyObject(foo);
// ...
delete po;
// ...
po->doSomething(); // segfault

但正如你在提到的问题中所说的那样,这不仅仅是因为使用参考资料更安全......

MY2C

答案 7 :(得分:0)

假设您没有语言中的引用运算符<type>&。然后,只要您想将对象的引用传递给函数,就必须执行C中的操作,将参数作为&<myobject>传递,并在函数中将其作为指针参数<type>* p接收。然后你必须训练自己而不是来做p++之类的事情。

这就是安全的来源。 通过引用传递非常有用,并且使用引用运算符可以避免您修改指针的风险。

(也许一个const指针会完成同样的事情,但你必须承认&更清晰。它可以应用于除参数之外的其他变量。)

答案 8 :(得分:-1)

指针是一个独立变量,可以重新分配以指向另一个日期项,未初始化的内存,或者根本不指向任何位置(NULL)。指针可以递增,递减,从相同类型的另一个指针中减去,等等。引用与现有变量相关联,只是变量名的别名。