Передача по ссылке с использованием указателя и разыменования

Какой из них лучше: пройти по ссылке, используя void increment(int& x) или пройти через указатель с помощью void increment(int* x)?

  1. void increment(int& x)
    {
        x=x+1;
    }
    int main()
    {  
        int n= 2;
        increment(n);
        cout << "Value of n is " << n << '\n';
        return 0;
    }
    

    или же

  2. void increment(int* x)
    {
        *x=*x+1;
    }
    int main()
    {  
        int n= 2;
        increment(&n);
        cout << "Value of n is " << n << '\n';
        return 0;
    }
    

4 ответа

Нет лучше. Преимущество использования указателя состоит в том, что в вызывающем коде явно указывается, что аргумент передается указателем, поскольку перед аргументом стоит &, Преимущество использования ссылки в том, что она более естественна в коде вызывающей функции без * разыменовывает. Внутренне они обычно реализуются таким же образом, поэтому не должно быть никаких преимуществ в скорости.

Не зная, что вы думаете "лучше", я могу только сказать, что они разные.

Плюсы прохождения по ссылке:

  1. Передача по ссылке помешает вам пройти nullptr и усложнит передачу неверного значения.

С точки зрения производительности, вы не увидите большой разницы, если таковой будет.

Плюсы прохождения по указателю:

  1. Передача по ссылке не совместима с C, поэтому, если вы предоставляете функцию, которую вы ожидаете вызвать из C, вам нужно заставить ее передавать указатель.

  2. Передача указателя позволяет вам пройти nullptr/NULL, Хотя перечисление этого преимущества может показаться противоречащим перечислению невозможности сделать это преимуществом ссылок, оно зависит от того, как вы используете свою функцию. Если вы хотите иметь возможность указать, что для параметра нет значения (и не хотите включать boost::optional или сверните свое собственное), тогда указатель - идиоматический способ сделать это.

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

Мое личное правило:

  • Используйте указатели для параметров (in/)out, поэтому сразу становится ясно, что функция изменит переменную
  • Используйте ссылки для нормальных параметров

Так что в вашем случае, где функция increment() Ясно изменить переданную переменную, я бы использовал указатель. Если вы используете ссылку, кто-то, читающий ваш код, должен будет перейти к объявлению функции, чтобы понять побочный эффект функции.

Для внешнего компонента (т. Е. При написании библиотеки потребляют третьи стороны), я бы взял указатель, он дает пользователям знать, что вы собираетесь изменить то, что они передают. Это происходит за счет разыменования указателя, которое имеет очень очень незначительные затраты производительности - так как вы захотите проверить указатель не NULL перед его использованием.

Для внутренних функций, которые не предназначены для показа, предпочтительнее передавать по ссылке, так как это проще для записи и (опять же, очень незначительно) более производительно.

С лязгом и без оптимизаций:

00000000004004c0 <_Z10doThingRefRi>:
  4004c0:       48 89 7c 24 f8          mov    %rdi,-0x8(%rsp)
  4004c5:       48 8b 7c 24 f8          mov    -0x8(%rsp),%rdi
  4004ca:       8b 07                   mov    (%rdi),%eax
  4004cc:       05 01 00 00 00          add    $0x1,%eax
  4004d1:       89 07                   mov    %eax,(%rdi)
  4004d3:       c3                      retq
  4004d4:       66 66 66 2e 0f 1f 84    data32 data32 nopw %cs:0x0(%rax,%rax,1)
  4004db:       00 00 00 00 00

00000000004004e0 <_Z10doThingPtrPi>:
  4004e0:       48 89 7c 24 f8          mov    %rdi,-0x8(%rsp)
  4004e5:       48 81 7c 24 f8 00 00    cmpq   $0x0,-0x8(%rsp)
  4004ec:       00 00
  4004ee:       0f 84 0f 00 00 00       je     400503 <_Z10doThingPtrPi+0x23>
  4004f4:       48 8b 44 24 f8          mov    -0x8(%rsp),%rax
  4004f9:       8b 08                   mov    (%rax),%ecx
  4004fb:       81 c1 01 00 00 00       add    $0x1,%ecx
  400501:       89 08                   mov    %ecx,(%rax)
  400503:       c3                      retq
  400504:       66 66 66 2e 0f 1f 84    data32 data32 nopw %cs:0x0(%rax,%rax,1)
  40050b:       00 00 00 00 00

с лязгом и -O3

00000000004004c0 <_Z10doThingRefRi>:
  4004c0:       ff 07                   incl   (%rdi)
  4004c2:       c3                      retq
  4004c3:       66 66 66 66 2e 0f 1f    data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
  4004ca:       84 00 00 00 00 00

00000000004004d0 <_Z10doThingPtrPi>:
  4004d0:       48 85 ff                test   %rdi,%rdi
  4004d3:       74 02                   je     4004d7 <_Z10doThingPtrPi+0x7>
  4004d5:       ff 07                   incl   (%rdi)
  4004d7:       c3                      retq
  4004d8:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  4004df:       00
Другие вопросы по тегам