Tuesday, November 12, 2019

Константы в си++

Не совсем по теме, но тоже есть слово “const”, поэтому коротко рассмотрим и этот вариант – constexpr. Если наша цель перф, то один из способов оптимизировать код, это произвести вычисления на этапе компиляции, а не во время выполнения программы. Это возможно, когда код содержит описание логики с помощью выражений, в составе которых все переменные и функции “известны” на момент компиляции или могут быть вычислены на этапе компиляции. Тема не простая и требует отдельного разговора.

Далее будем рассматривать const в классическом понимании – мы обещаем не изменять наши переменные и хотим, чтобы компилятор следил за этим.

Константные переменные. Объявление переменной с помощью const, требует ее инициализации во время объявления и запрещает ее изменение в дальнейшем. Попытки это сделать приведут к ошибкам. ( const int x = 0; )

Немного сложнее с указателями. При использовании const слева от типа ( const int* pX = &x; ), мы запрещаем изменение значения, на которое указывает указатель. При использовании const справа от типа ( int* const pX = &x; ), мы запрещаем изменение самого указателя. Кстати для указателей можно использовать оба варианта одновременно ( const int* const pX = &x; ).

Но для референсной ссылки будет работать только первый вариант ( const int& rX = x; ), когда мы запрещаем изменение значения, т.е. переменной, на которую указывает ссылка. И тут важно понимать, что при присвоении ссылке нового значения, мы не меняем ссылку, а присваиваем новое значение переменной, на которую указывает ссылка. Это нам пригодится при рассмотрении возврата из функций в контексте констант.

Константы в функциях. При возвращении функцией указателя или референсной ссылки, мы превращаем вызов такой функции в lvalue, т.е. в переменную, которая может стоять слева от знака присваивания и соответственно мы можем присвоить этой переменной новое значение тем самым изменив ее. Если нам нужно защититься от этого, то мы можем указать const перед типом возвращающего значения функции ( const int& foo(){} ). Внимание, дополнительно тут хотелось бы отметить, что нельзя возвращать указатель или ссылку на локальную переменную из тела функции, которая уничтожается при выходе из функции.

Теперь давайте рассмотрим параметры функции, мы помним, что по умолчанию в си++ передача параметров в функции происходит по значению, т.е. внутри функции происходит копирование данных. Если данные “тяжелые”, это может потребовать значительных затрат. Чтобы избежать копирования, мы можем передавать данные по указателю или ссылке, что в свою очередь ставит их под угрозу изменения. Чтобы их защитить мы можем использовать константные указатель или ссылку ( void foo(const int&){} ). Через константную ссылку на объект класса можно вызвать только константный метод.

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

Перегуд В.

No comments: