C++ const ключевое слово - использовать свободно?

В следующих функциях C++:

void MyFunction(int age, House &purchased_house)
{
    ...
}


void MyFunction(const int age, House &purchased_house)
{
    ...
}

Что лучше?

В обоих случаях "возраст" передается по значению. Мне интересно, нужно ли ключевое слово "const": оно мне кажется избыточным, но также полезным (в качестве дополнительного указания переменная не будет изменяться).

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

12 ответов

Решение

Это, ИМХО, злоупотребляет. Когда вы говорите "const int age,...", на самом деле вы говорите: "Вы не можете изменить даже локальную копию внутри своей функции". На самом деле вы делаете код программиста менее читаемым, заставляя его использовать другую локальную копию, когда он хочет изменить возраст / передать его по неконстантной ссылке.

Любой программист должен знать разницу между передачей по ссылке и передачей по значению так же, как любой программист должен понимать "const".

Во-первых, это просто деталь реализации, и если вы поместите туда const, не помещайте ее в набор объявлений (заголовок). Только поместите это в файл реализации:

// header
void MyFunction(int age, House &purchased_house);

// .cpp file
void MyFunction(const int age, House &purchased_house);
{
    ...
}

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

Я не видел таких вещей часто, и я тоже не делаю этого. Наличие параметра const может сбить меня с толку чаще, чем справку, потому что я бы сразу сопоставил шаблон-совпадение-неудачу со значением "const int &age":) Конечно, дело совсем не в том, чтобы использовать const на другом уровне

// const is a *good* thing here, and should not be removed,
// and it is part of the interface
void MyFunction(const string &name, House &purchased_house);
{
    ...
}

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

Я рекомендую прочитать Херб Саттер. Исключительный C++. Есть глава "Const-Correctness".

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

Это означает, что эта подпись

void print(int number);

фактически так же, как это:

void print(int const number);

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

Читайте дальше, что Херб Саттер рекомендует:

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

Он рекомендует избегать этого:

void print(int const number);

Потому что это const сбивает с толку, многословно и излишне.

Но в определении вы должны сделать это (если вы не измените параметр):

void print(int const number)
{
   // I don't want to change the number accidentally here
   ...
}

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

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

См. Meyers и 'Effective C++' по этому вопросу, и используйте const свободно, особенно с семантикой передачи по ссылке.

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

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

Однако на более низком уровне const Модификатор, примененный к значению параметра, может иметь некоторые преимущества оптимизации, учитывая достаточно умный компилятор. Рассмотрим две функции с одинаковым набором параметров (для простоты)

int foo(const int a, const double b, const long c) {
   /* whatever */
}

void bar(const int a, const double b, const long c) {
   /* whatever */
}

Допустим, где-то в коде они называются следующим образом

foo(x, d, m);
bar(x, d, m);

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

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

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

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

Второй вариант лучше. В первом вы можете случайно изменить переменный возраст.

Одно из преимуществ использования const является то, что вы не можете случайно изменить значение age во время MyFunction (если вы забыли, это не было передано по ссылке). Один "недостаток" в том, что вы не можете утилизировать age с кодом вроде foo.process(++age);,

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


Могу ли я предложить вам следующую максиму?

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

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


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

.cpp

void WriteSequence(int const *, const int);

int main()
{
    int Array[] = { 2, 3, 4, 10, 12 };
    WriteSequence(Array, 5);
}


#include <iostream>
using std::cout;
using std::endl;
void WriteSequence(int const *Array, const int MAX)
{
    for (int const * i = Array; i != Array + MAX; ++i)
      cout << *i << endl;
}

Что бы произошло, если бы я удалил const перед int MAX и написал MAX + 1 внутри вот так?

void WriteSequence(int Array[], int MAX)
{
    MAX += MAX;
    for (int * i = Array; i != Array + MAX; ++i)
      cout << *i << endl;
}

Что ж, ваша программа потерпит крах! Теперь, зачем кому-то писать "MAX += MAX;"? Возможно, человеческая ошибка, может быть, программист не чувствовал себя хорошо в тот день или, возможно, программист просто не знал, как писать код на C/C++. Если бы вы имели const, код даже не скомпилировался бы!

Безопасный код - это хороший код, и его добавление "const" ничего не стоит, когда оно у вас есть!


Вот ответ из другого поста на очень похожий вопрос:

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

Неправильно.

Речь идет о самодокументировании вашего кода и ваших предположений.

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

Кроме того, как кто-то упоминал ранее, это может помочь компилятору немного оптимизировать вещи (хотя это далеко).

Вот ссылка: ответ.

Вы должны использовать const для параметра if (и только если) вы бы использовали его для любой другой локальной переменной, которая не будет изменена:

const int age_last_year = age - YEAR;

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

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

Итак, либо:

void MyFunction(const int age, House &purchased_house)
{
    const int age_last_year = age - YEAR;
}

или же:

void MyFunction(int age, House &purchased_house)
{
    int age_last_year = age - YEAR;
}

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

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

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

Другие вопросы по тегам