Страницы

Поиск по вопросам

понедельник, 3 февраля 2020 г.

Передача интегральной переменной в функцию: по значению или по ссылке?

#cpp


f(int i) vs f(const int& i)

есть ли смысл в f(const int& i) ?
    


Ответы

Ответ 1



В случае int нет никакого - потому что указатель может быть "длинее". Сам инт обычно 32бита, а указатель может быть от 32 до 64 бит (в зависимости от платформы и компилятора). Потестируйте, я думаю, что разницы не будет, возможно, даже второй вариант окажется медленее. Посмотрим код по факту int f1(int a) { return a*a; } int f2(const int &a) { return a*a; } и результат f1(int): # @f1(int) imull %edi, %edi movl %edi, %eax retq f2(int const&): # @f2(int const&) movl (%rdi), %eax imull %eax, %eax retq код очень похожий, но в первом случае компилятор смог подставить значение сразу с регистра. В втором случае он вставляет ещё загрузку с памяти. Нужно конечно смотреть в мануалы, но если бы это был старый процессор виду 286-386, то первый вариант работал бы быстрее. (да, значние в регистр нужно ещё занести, но компилятор может больше творить). А вот если вместо int сложный объект, с сложным конструктором, выигрыш может быть очень существенный. Ведь нужно копировать объекты, освобождать память и многое другое. В моей практике был случай, когда в функцию передавалось два аргумента, которые были просто int a[4]. Сделав указанную выше оптимизацию, я получил около 20 раз ускорение (да, функция была небольшой и очень часто вызывалась).

Ответ 2



Разумеется, разница есть. Имея const int&, кто угодно может сбросить const при помощи const_cast, и поменять переменную. Пример: #include using namespace std; void f1(int x) { x = 5; } void f2(const int& x) { const_cast(x) = 5; } int main() { int i = 0; cout << "start: " << i << endl; f1(i); cout << "after f1: " << i << endl; f2(i); cout << "after f2: " << i << endl; return 0; } Результат: start: 0 after f1: 0 after f2: 5 С передачей копии таких проблем возникнуть не может.

Ответ 3



Передача переменной по ссылке обладает одним особым свойством: если выполнены условия, при которых ссылка привязывается к передаваемому объекту напрямую, то она сохраняет адресную идентичность объекта. Например int i; void foo(const int &ri) { assert(&i == &ri); } int main() { foo(i); } А уж важна ли вам такая адресная идентичность - это уже вам решать. Если не важна, то никакого смысла передавать скалярные объекты по ссылке нет.

Ответ 4



В объявлении f(const int& i); смысла нет, так как обычно ссылки внутренне передаются как указатели на объект, а sizeof( int * ) может превосходить по величине sizeof( int ) и внутри кода функции для обращения к объекту могут использоваться команды косвенного обращения вместо прямого обращения к локальной переменной, коей является параметр функции. Другое дело, если вы пишете шаблонную функцию с параметром вида const T &, где T в общем случае может быть любым типом, а не только целочисленным, то в этом имеется большой смысл, так как вызов по ссылке позволяет избежать создание временного объекта и, соответственно, вызовов конструкторов и деструкторов. Кроме того пользовательские типы могут быть к тому же некопируемыми.:)

Ответ 5



Формально есть. В первом случае параметр передается по значению (с созданием копии). В случае с константной ссылкой, параметр передается собственно по ссылке, и, следовательно, не создается его копия. Фактическое различие проявляется при передаче аргументов, которые не являются POD-типами, так как исчезают затраты на копирование при использовании способа 2.

Комментариев нет:

Отправить комментарий