Может ли ссылка быть более эффективной, чем указатель? - PullRequest
7 голосов
/ 29 мая 2011

Мне интересно, может ли иногда (зависит от платформы, компилятора или контекста в коде и т. Д.) Ссылка быть более эффективной, чем указатель?

Ответы [ 6 ]

10 голосов
/ 29 мая 2011

Может ли ссылка быть более эффективной, чем указатель?

Nopes! Не обязательно. Стандарт также этого не говорит.

Большинство компиляторов реализуют ссылочный механизм с помощью указателей. Ссылки были добавлены в C ++ для поддержки перегрузки операторов , а не для эффективности.

8 голосов
/ 29 мая 2011

Да, может. Единственный способ, которым мы могли бы сказать «это невозможно», - это если в стандарте было требование (явное или подразумеваемое), что ссылки должны быть такими же медленными или медленными, как эквивалентные указатели во всех случаях. Такого требования нет, поскольку стандарт не касается такого рода деталей производительности.

Например, при низких уровнях оптимизации было бы разумно для этого:

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

быть немного медленнее, чем это:

int n = 0;
int &nr = n;
for (int i = 0; i < 10000000; ++i) {
    nr += i;
}
std::cout << n;

просто потому, что в первом случае компилятору немного проще обнаружить, что он может полностью избежать косвенного обращения - nr явно является псевдонимом для n везде, где он находится в области видимости, тогда как *np именно так и происходит оставаться псевдонимом для n, потому что мы никогда не присваиваем np после его инициализации. Результатом с низкой оптимизацией может быть больше косвенности в случае указателя и менее эффективный цикл.

Взгляните на код, который выдает ваш компилятор, хотя - gcc без оптимизации выдает для меня один и тот же код, похоже, что он обнаружил псевдоним в обоих случаях и использует регистр для общего значения и слот стека для i.

Конечно, вы ожидаете, что хороший оптимизирующий компилятор "поймет" псевдонимы в обоих случаях и выдаст код, который хранит n в регистре и касается только этого регистра (но не памяти) в цикле.

5 голосов
/ 29 мая 2011

Вы можете передать NULL указателю, поэтому вы должны проверить, равна ли переменная указателя NULL.Это единственная причина, почему ссылка немного более эффективна.

Кроме того, компиляторы реализуют ссылки как указатели.

4 голосов
/ 30 мая 2011

Да, ссылки могут быть более эффективными, чем указатели. Причина кроется в оптимизаторах. В общем, одна из самых дорогих операций в современном процессоре - это загрузка из памяти. И после того, как вы вызвали (не встроенную) функцию, компилятор должен предположить, что любая переменная в памяти, которую могла изменить, изменилась. Это означает, что в следующем примере он должен перезагрузить p после вызова Bar():

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

Теперь проблема не в том, что *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; }
};

Теперь компилятор видит, что p не используется после Bar(). r есть, и, вероятно, он реализован как указатель. Но это не может быть переназначено! Поэтому оптимизатор имеет возможность сохранения r через вызов функции.

4 голосов
/ 29 мая 2011

Ничто не может быть более эффективным, чем указатель, за исключением прямого доступа.Но скорость это не то, почему ссылки существуют.

3 голосов
/ 29 мая 2011

C ++ Ссылки работают как «постоянно разыменованные» указатели под капотом.Я ожидаю, что производительность будет такой же.Однако, поскольку указатель ссылки не может быть переназначен, компилятор может применить больше оптимизаций, которые нельзя использовать для необработанных указателей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...