Я пытаюсь выяснить, когда const следует использовать при написании кода C ++. Это все примеры пессимизации или так полезно писать код ?:
Пример 1:
int findVal(const int OTHER_VAL) const
{
switch(OTHER_VAL)
{
case 1:
return 2;
default:
return 3;
}
}
Пример 2:
enum class MobType
{
COW, CHICKEN, DOG, PIG
};
class BaseMob
{
protected:
BaseMob(const MobType TYPE) : TYPE(TYPE) { }
const MobType TYPE;
};
Пример 3:
void showWorld(const World &world)
{
auto data = world.getData();
for (auto &i:data)
i.print();
}
Нет, это не так.
const
Локальные переменные с автоматическим хранением (включая аргументы функций) являются чисто синтаксическим сахаром, помогающим программистам-людям устанавливать правила для своего кода. Это не помогает оптимизатору вообще. Оптимизирующие компиляторы извлекают необходимое перемещение данных из источника C и оптимизируют его. Как правило, им все равно, если вы повторно используете одну и ту же переменную tmp для множества разных вещей или у вас есть 10 разных const tmp1 = a+10;
в той же функции.
И да, это относится к аргументам функции, переданным по значению; это локальные переменные с автоматическим хранением, передаваемые в регистрах или в стеке. И нет, это не означает, что вызывающая сторона может предположить, что функция не модифицировала стековую память, используемую для передачи аргументов, поэтому Помогите оптимизатора тоже много. (Выполнение второго вызова функции с теми же аргументами все еще требует перезаписи аргументов в стек (если не все аргументы помещаются в регистры), потому что const
Аргумент arg не меняет того факта, что вызываемая функция «владеет» этим пространством стека и может использовать его как пустое место, как захочет.)
const
на статические / глобальные / ссылочные переменные помогает. static const int foo = 10;
может быть встроен как непосредственная константа вместо загрузки из памяти. (например. add eax, 10
вместо add eax, [foo]
).
С помощью const
пометить метод класса как не меняющий членов класса может также помочь компилятору избежать повторной загрузки членов класса после вызова функции. (т.е. держать их вживую в регистрах). Это в основном применимо только в том случае, если компилятор не может увидеть определение функции, в противном случае хороший оптимизирующий компилятор может просто посмотреть на то, что делает вызываемая функция, и соответственно оптимизировать. (Пока это не в библиотеке Unix, где символьное расположение означает, что он не может предположить, что вызванная функция, которую он видит во время компиляции, будет вызвана после динамического связывания.)
Всякий раз, когда вы логически не изменяете значение или объект, вы должны сделать это const
, Логически я имею в виду не каждый раз, когда вам это технически разрешено, но каждый раз, когда это логично в контексте ваших функций, классов и кода.
Простым примером может быть простая функция «get», как видно в примере 1, эти функции не должны изменять состояние класса, и поэтому должны быть помечены как постоянные, поскольку это поможет документировать ваше намерение для пользователя, а также поможет вам обеспечить инвариантность класса.
Существуют ситуации, когда имеет смысл создать неизменный объект, как показано в примере 2. Мы не так часто видим их в C ++, но многие другие языки часто их используют. Если он не добавляет никакого значения, чтобы иметь возможность изменять определенный элемент в течение времени жизни объекта, вы можете также сделать его константой.
Передача константных параметров ссылки дает вам преимущества производительности ссылки, но в то же время гарантирует, что исходный объект остается неизменным, что является отличной документацией для пользователя, но также позволяет выполнять некоторые оптимизации.
Упомянув все эти причины, есть и другие const
как кратко упомянуто в последнем абзаце, оптимизации. Когда компилятор знает, что что-то является постоянным и не изменяется, он может включить некоторые довольно умные оптимизации, не используйте const
хотя по соображениям производительности.
Это также то, почему работа вокруг константы (например) const_cast
литье, которое можно отбросить const
, может привести к некоторому нежелательному поведению. В качестве примера проверьте следующее:
#include <stdio.h>
static const int foo = 10;
int constsum(void) {
return foo + 5;
}
int main(int argc, char* argv[]) {
int a = constsum();
int* newFoo = const_cast<int*>(&foo);
*newFoo = 20;
int b = constsum();
printf("%d\n", a + b);
return 0;
}
Как видно из этого примера (увидеть код здесь) это может не дать желаемого результата, так как результат кода в 30
быть напечатанным, а не как ожидалось 40.
При осмотре произведенной сборки видно почему (составлен в сборку):
constsum():
mov eax, 15
ret
main:
mov eax, 30
ret
Компилятор просто вставляет значения, так как он может видеть, что они постоянны, он не заботится о том, чтобы const_cast
используется.
Так что const правильность и использование const
это ценный инструмент, который может повысить производительность и стабильность вашего кода, но также (и не забывать) помогает документировать ваш код.