Почему я не должен использовать `const` в этой простой функции?

При изучении С++ одна из первых функций, получаемых для изучения концепции функции, - это что-то вроде

int add(int a, int b)
{
    return a+b;
}

Теперь мне было интересно: следует ли использовать const -keyword здесь, или, скорее, нет, в результате чего

int add(const int a, const int b)
{
    return a+b;
}

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

Ответ 1

С точки зрения вызывающего абонента первая и вторая форма одинаковы.

Так как целые числа передаются по значению, даже если функция изменяет a и b, измененные значения являются копиями оригинала и не будут видны вызывающему.

Однако, с точки зрения функции реализации существует разница. Фактически, во второй форме:

int add(const int a, const int b)

вы получите ошибку компилятора, если попытаетесь изменить значения a и b внутри тела функции, поскольку они отмечены как const.

Вместо этого вы можете изменить эти значения, если вы опустите const.
Опять же, эти изменения не будут видны вызывающему.

Ответ 2

Использование int add(const int a, const int b) означает, что вы не можете изменять входные параметры в теле функции.

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

Передача параметров const также может повысить стабильность вашей базы кода, особенно в совместном проекте.

Все это сказало, хотя, я считаю, что это слишком многословно, а не излишне, и будет использовать int add(int a, int b). Очень часто для особо длинных функций я использую тот факт, что вы можете объявить функцию с параметрами не const и определить ее с параметрами const.

Ответ 3

Если вы действительно боретесь с ошибками правильности в своей кодовой базе, где const помогли бы, добавьте const.

Тем не менее, есть связанные проблемы, которые вы должны рассмотреть. Квалификаторы функциональных параметров верхнего уровня не являются частью типа функции, поэтому ваш тип функции по-прежнему остается int(int, int). (Определители влияют только на переменные параметра в определении функции.) Это означает, что любое объявление функции также игнорирует квалификаторы, поэтому int add(int, int) и int add(const int, int) и int add(int, const int) все объявляют одну и ту же функцию. Поэтому вам нужно решить, как вы пишете файлы заголовков. И теперь у вас есть три основных положения, которые вы можете взять:

  • Всегда квалифицируйтесь как в декларации, так и в определении. Положительным моментом является то, что это может привести к тому, что код выглядит "согласованным" (подумайте о копировании/вставке при создании реализаций). Недостатком является то, что квалификаторы не имеют никакого отношения к интерфейсу и совсем не являются принудительными (вы можете изменить определение позже!), Поэтому в лучшем случае это шум, в худшем случае это неправильно.

  • Откажитесь от определения, но не в других объявлениях. Поверхность заключается в том, что это правильно связывает интерфейс, и вы все еще получаете проверку константы в определении. Недостатком является то, что некоторые люди могут быть смущены расхождениями в написании. (Так же, как люди могут быть смущены тем, что член класса static constexpr T foo; может быть определен с помощью const T C::foo;.)

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

Нет правильного ответа. Если вы являетесь владельцем кодовой базы или руководством проекта, вы должны решить, исходя из того, какие самые большие проблемы в вашей кодовой базе и команде. (Мое личное положение состоит в том, чтобы придерживаться (3), пока у вас не будет веских оснований для изменения.)

Ответ 4

Я чувствую, что все танцуют вокруг этой части ответа... Это правда, что использование const будет удерживать функцию от изменения значения ваших int a и b, находясь внутри функции. Это может быть очень полезно, поэтому используйте его по своему усмотрению, что позволяет компилятор. Но вызывающий функция никогда не узнает о каких-либо изменениях в a и b после завершения функции. Поэтому, даже если a и b изменены, никто, кроме определенной функции, не узнает их обновленные значения.

int funcB(int a, int b)
{
    a = a+1;
    b = b*b;
    return a+b;
}

void funcA()
{
    int s = 5;
    int t = 6;
    int result = funcB(s, t);
    printf("%f + %f = %f", s,t, result);
}

funcA выводит: "5 + 6 = 42"

const защита часто используется при передаче значений по ссылке, то есть:

int function(const int &a, const int &b) {}

Это передает функцию a и b функции (т.е. не делает копии a и b, а передает только адрес памяти этой переменной, ака: дескриптор). При передаче переменной по ссылке любые изменения, внесенные в переменную, запоминаются вне области действия функции и могут изменять способ запуска вашей программы. Это, как правило, нежелательное поведение. Итак, если вы переработаете funcB сверху и пройдете по ссылке:

int funcB(int &a, int &b)
{
    a = a+1;
    b = b*b;
    return a+b;
}

funcA выводит: "6 + 36 = 42"

Если вы добавили const правильность в funcB:

int funcB(const int &a, const int &b)
{
    a = a+1;
    b = b*b;
    return a+b;
}

Я не думаю, что компилятор позволит вам это сделать, так как вы явно пытаетесь изменить значения, защищенные с помощью const.

В другой раз, когда может быть действительно важно использовать константу, это когда вы передаете указатель вместо ссылки или копии...

int funcB(int *a, int *b)
{
    a = a+1;
    b = b*b;
    return a+b;
}

Если вы не являетесь экспертом-указателем, избегайте прокручивания указателей без const-претензий. Эта функция, скорее всего, попытается выполнить итерацию индекса ваших указательных массивов, и вы откроете себя для запуска временных ошибок, связанных с отсутствием связанной памяти. Вы можете случайно увидеть память из совершенно другой программы... но, вероятно, нет.

Наконец, поскольку вы просто проходите int, нет никакой практической необходимости передавать по ссылке (что часто делается для того, чтобы не добавлять сложные данные в память, потому что каждый нережим или не указатель переходит к функциям, копирует значение в память для жизни вызываемой функции), так как площадь памяти int настолько мала. Если вы не работаете со специализированным оборудованием, которое имеет чрезвычайно ограниченную память, тогда это может быть полезно; это не будет применяться к большинству стандартных компьютеров и настольных компьютеров, созданных за последние 20 лет, или к смартфонам.

Ответ 5

int add(const int a, const int b)
{
    return a+b;
}

Здесь const используется так, чтобы функция не изменяла исходные значения a и b случайно. В приведенном выше примере это не имеет смысла. Но если бы это был пример вроде

int add(const int *a, const int *b)
{
    //*a = 10; //This will throw error. 
    return a+b;
}

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

Ответ 6

Если вы хотите быть поистине const правильным, тогда вы должны добавить его, но на самом деле все, что он сделает, это заставить вас вводить и читать больше.

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

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

Ответ 7

Вы можете использовать const там, если хотите.

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

a и b являются int, которые передаются по значению, поэтому их const не влияет на пользователей этой функции.

Единственное возможное преимущество заключается в том, что ваша функция длинная и сложная, и вы хотите избежать возможных ошибок программирования, когда исходные значения a или b изменяются во время функции.

Ответ 8

Если вы будете использовать const, вы не сможете изменить значение a, b. Вот почему мы не используем const.

Ответ 9

Основная цель Constance - предоставить документацию и предотвратить ошибки программирования. Const позволяет вам разъяснять себе и другим, что что-то не следует менять. Более того, у него есть дополнительное преимущество: все, что вы объявляете const, на самом деле останется без использования силовых методов. Особенно полезно объявлять ссылочные параметры к функциям как ссылки const:

bool SomeFunction (const myObj& obj);

Здесь объект myObj передается ссылкой в ​​SomeFunction. Для безопасности const используется для того, чтобы SomeFunction не мог изменить объект. В конце концов, он просто должен убедиться, что объект находится в правильном состоянии. Это может предотвратить ошибки глупого программирования, которые в противном случае могут привести к повреждению объекта (например, путем установки поля класса для целей тестирования, что может привести к тому, что поле никогда не будет reset). Более того, объявляя аргумент const, пользователи функции могут быть уверены, что их объект не будет изменен и не нужно беспокоиться о возможных побочных эффектах вызова функции.

Кроме того, причина в том, что параметр const для параметра применяется только локально внутри функции, поскольку он работает с копией данных. Это означает, что сигнатура функции действительно одинакова. Это, вероятно, плохой стиль, чтобы сделать это много. Я лично склонен не использовать const, кроме ссылочных и указательных параметров.

Ответ 10

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

второе: a и b передаются по значению, поэтому они создаются как локальные константы. Но до тех пор, пока они не изменяются, зачем нужны копии?

  • Хорошая вещь - передать ссылку на константу, которая означает никогда не изменять параметры и избегать копий (по значению):

    int add(const int& a, const int& b) // now there are no copies of a and b in addition we cannot modify a and be
    {
        return a+b;
    }