Ключевое слово Const и указатели
Если нужно передать массив в функцию так, чтобы можно было менять элементы массива, пользуются таким синтаксисом:
void show(unsigned char * block)
Однако, если массив просто передается, и его элементы не должны изменяться, правильнее будет использовать модификатор const:
void show(const unsigned char * block)
С модификатором const есть много путанницы. Вопрос: что значит запись "const unsigned char *" - это запрет модификации элементов массива, или запрет модификации указателя на массив? Правильный ответ - запрет модификации элементов массива. Чтобы понять, почему так, нужно знать, что на самом деле, такая запись - это историческое "расширение" синтаксиса C++. В первых версиях стандарта такого синтаксиса предусмотрено не было. Предолагалась только четкая запись в виде:
<Тип> <Модификатор доступа>
и правильная запись (эквивалент вышеприведенной) будет выглядеть так:
void show(unsigned char const * block)
То есть, модификатор доступа const влияет на тип, написанный слева от него, вот и все дела. То есть, данная запись означает, что константным будет тип unsigned char, таким образом неизменными будут оставаться данные массива, а не указатель на массив (ведь звездочка осталась справа). Но потом в стандарт была добавлена возможность написания модификатора доступа в начало определения, и однозначность сразу исчезла. Теперь модификатор доступа const пишется по такому правилу:
1. Модификатор доступа const пишется после типа, и, соответственно, влияет на тип, расположенный слева от него.
2. Но если const написан в начале определения, то он влияет на "короткий" тип справа от него. |
"Короткий тип" - это имеется в виду минимально возможное написание типа, которое расположено справа от const. То есть, в нашем примере "const unsigned char * block", минимально возможный тип будет unsigned char без звездочки. Таким образом получаем, что запись вида "const в начале" говорит о невозможности изменить сами данные в массиве, а не невозможность изменить указатель на массив.
Пользуясь правилом, становится понятно, как написать невозможность изменения указателя на массив. Вот так:
void show(unsigned char * const block)
Здесь работает первая часть правила. Можно написать невозможность изменения как данных в массиве, так и указателя на массив:
void show(unsigned char const * const block)
Здесь тоже работает первая часть правила. Но на практике чаще будут встречаться описания с ведущим const (по второму пункту правила):
// Неизменны только данные массива
void show(const unsigned char * block)
// Неизменны и данные массива и указатель
void show(const unsigned char * const block)
В последнем примере часть const unsigned char относится ко второму пункту правила, а часть * const - к первому. Да, это неудобно, но почему-то так принято.
Ключевое слово Const и ссылки
В отличие от указателей, ссылки по своей природе обладают таким важным свойством, как невозможность изменения адреса, куда указывает ссылка. Поэтому ссылка инициализируется сразу при создании, и нет возможности задать ей место, куда она должна указывать, где-либо еще в коде. Поэтому никакого смысла в модификаторе, который бы блокировал изменение именно ссылки (а не значения, на которое она указывает) нет.
Поэтому имеет смысл говорить только о синтаксисе параметров функций, который бы блокировал изменение значения, на которое указывает ссылка. И этот синтаксис полностью эквивалентен синтаксису указателей, только вместо звездочки (*) используется амперсанд (&).
Следующие прототипы функций полностью эквивалентны:
void showValue(const double & value)
void showValue(double const & value)
Такая форма записи запрещает изменять значение, на которое указывает ссылка.
Хорошим тоном считается писать модификатор const для ссылок, у которых значение, на которое она указывает, не должно меняться внутри функций.