参考可能比指针更有效吗?

Gui*_*e07 7 c++ performance

我想知道有时候(取决于平台或编译器或代码中的上下文等)引用是否比指针更有效?

Pra*_*rav 10

参考可能比指针更有效吗?

Nopes!不必要.标准也没有说.

大多数编译器通过使用指针实现引用机制.引用被添加到C++中以支持运算符重载而不是效率.

  • @Guillaume07:Bjarne Stroustrup[C++ 历史:1979−1991] 说“引入引用主要是为了支持运算符重载”。C 按值传递每个函数参数,如果按值传递对象效率低下或不合适,则用户可以传递指针。该策略在使用运算符重载的情况下不起作用。在这种情况下,符号的便利性至关重要,因此如果对象很大,则不能期望用户插入运算符的地址。 (2认同)

Ste*_*sop 8

是的,可能.我们可以说"它不能"的唯一方法是,如果标准中有一个要求(显式或暗示),则引用在所有情况下都比等效指针慢或慢.没有这样的要求,因为标准并不涉及这种性能细节.

例如,在低优化级别下,这是合理的:

int n = 0;
int *np = &n;
for (int i = 0; i < 10000000; ++i) {
    *np += i;
}
std::cout << n;
Run Code Online (Sandbox Code Playgroud)

比这慢一点:

int n = 0;
int &nr = n;
for (int i = 0; i < 10000000; ++i) {
    nr += i;
}
std::cout << n;
Run Code Online (Sandbox Code Playgroud)

只是因为在第一种情况下,编译器检测到它可以完全避免间接的情况下更容易一点 - nr显然是n它在范围内的任何地方的别名,而*np恰好是n因为我们从未np在它之后分配的别名已初始化.低优化的结果可能在指针情况下更加间接,并且循环效率较低.

看看你的编译器发出的代码,但是 - 没有优化的gcc为我发出相同的代码,它看起来在两种情况下都发现了别名,并使用寄存器来获取总数和堆栈槽i.

当然,你期望一个好的优化编译器能够"理解"两种情况下的别名,并发出存储n在寄存器中的代码,并且只接触循环中的寄存器(而不是内存).


MSa*_*ers 6

是的,引用可以比指针更有效。原因在于优化器。通常,现代 CPU 中最昂贵的操作之一是从内存加载。在您调用(非内联)函数之后,编译器必须假设内存中任何可能更改的变量确实发生了更改。这意味着在以下示例中,它必须p在调用后重新加载Bar()

class Foo {
  int* p;
  void Bar(); // defined somewhere else, not visible to compiler.
public:
  Foo() { p = new int; Bar(); std::cout << *p; }
};
Run Code Online (Sandbox Code Playgroud)

现在,这里的问题不是*p必须重新加载,而是p必须重新加载。它也有机会。现在看下面的代码:

class Foo {
  int* p; // !
  void Bar(); // defined somewhere else, not visible to compiler.
public:
  Foo() { p = new int; int& r = *p; Bar(); std::cout << r; }
};
Run Code Online (Sandbox Code Playgroud)

现在,编译器发现pBar(). r是,并且它可能被实现为一个指针。但不能重新分配!因此,优化器可以选择在r整个函数调用中保存。


BЈо*_*вић 5

您可以将NULL传递给指针,因此您必须检查指针变量是否为NULL.这是参考更高效的唯一原因.

除此之外,编译器正在将引用实现为指针.

  • 你没有*要检查.对于NULL输入,您可以将函数的行为记录为未定义. (2认同)