const в C++: понятное руководство по константности (const correctness) с примерами
Запрос «const в C++» — один из самых частых у начинающих. Константность (const correctness) помогает сделать код безопаснее: защищает от случайной модификации данных, упрощает оптимизацию и улучшает читаемость API. Ниже — практическое руководство с примерами и советами для реальных проектов.
Что такое const в C++
Ключевое слово const делает объект неизменяемым в пределах области видимости. Примеры:
const int a = 10; // a нельзя изменить
int const b = 20; // одинаково с const int b
// Важно: const влияет на тип.
// top-level const — константность самого объекта;
// low-level const — константность того, на что указывает указатель/ссылка.
const и указатели: частые вопросы
Правило чтения: const «приклеен» к тому, что справа. Если справа ничего — смотрим слева.
int x = 1, y = 2;
const int* p1 = &x; // pointer to const int (нельзя менять *p1, но можно p1)
int* const p2 = &x; // const pointer to int (нельзя менять p2, но можно *p2)
const int* const p3 = &x; // const pointer to const int
*p1 = 42; // ОШИБКА: *p1 — const
p1 = &y; // OK
*p2 = 42; // OK
p2 = &y; // ОШИБКА: p2 — const
Ссылки всегда «константа-ссылка» (их нельзя переназначить), поэтому чаще пишут const T& для «только чтения» без копирования.
Константные методы и перегрузка по const
Метод с суффиксом const обещает не менять логическое состояние объекта (все поля, кроме помеченных как mutable).
struct Vec2 {
double x{0}, y{0};
double length() const { // метод "только чтение"
return std::sqrt(x*x + y*y);
}
void setX(double nx) { x = nx; } // изменяет объект
};
void demo() {
const Vec2 v{3, 4};
double L = v.length(); // OK
// v.setX(10); // ОШИБКА: метод не const
}
Можно перегружать методы по константности — удобно, когда для const-объекта возвращаем "чтение", а для неконстантного — "чтение+запись":
class Buffer {
std::vector<int> data;
public:
const int& operator[](size_t i) const { return data[i]; }
int& operator[](size_t i) { return data[i]; }
};
void use(Buffer& b, const Buffer& cb) {
int a = cb[0]; // возвращает const int&
b[0] = 42; // возвращает int& — можно менять
}
Параметры и возвращаемые значения
- Мелкие типы (int, double, enum, указатели) передавайте по значению.
- Крупные объекты — по const T& для избежания копирования.
- Возвращать по значению — нормально; добавлять const к возвращаемому значению, как правило, бессмысленно.
// Хорошо: без лишних копий и с ясной семантикой
std::string_view Find(const std::string& s, char ch);
// Избыточно: const при возврате по значению не даёт пользы
const std::string MakeName(); // лучше просто std::string
Если нужна «глубокая» неизменяемость результата — возвращайте const T& только если гарантируете время жизни объекта (часто это член класса или статик).
const и STL: итераторы и диапазоны
Используйте const-итераторы и cbegin/cend для обхода без модификации:
std::vector<int> v{1,2,3};
for (auto it = v.cbegin(); it != v.cend(); ++it) {
// *it — const int&
}
for (const auto& value : v) { // range-based for + const auto&
// value — const int&
}
mutable и const_cast: когда уместно
Иногда «константный» метод хочет кэшировать результат. Для этого есть mutable — поле можно менять даже в const-методе. Делайте так только для прозрачных оптимизаций.
class Heavy {
mutable bool cached{false};
mutable double memo{0};
public:
double value() const {
if (!cached) {
memo = /* тяжёлый расчёт */ 42.0;
cached = true;
}
return memo;
}
};
const_cast снимает константность, но использовать его нужно крайне осторожно. Если вы меняете объект, который изначально был действительно const, — поведение неопределено.
void f(const int* p) {
// ПЛОХО: если p указывает на действительно const-объект
int* q = const_cast<int*>(p);
// *q = 123; // может привести к UB
}
constexpr vs const
- const — неизменяемость во время выполнения.
- constexpr — значение вычисляется на этапе компиляции (если возможно). Любой constexpr по определению также const.
const double Pi = 3.1415926535; // константа во время выполнения
constexpr int N = 2 * 3 * 5; // вычислено на этапе компиляции
Мини-пример: конфиг только для чтения
Соберём простой класс, где const correctness делает интерфейс очевидным.
class Config {
std::map<std::string, std::string> kv;
public:
explicit Config(std::map<std::string, std::string> init)
: kv(std::move(init)) {}
// Чтение без изменений
const std::string& get(const std::string& key) const {
static const std::string empty;
if (auto it = kv.find(key); it != kv.end()) return it->second;
return empty;
}
// Изменение доступно только через неконстантный метод
void set(std::string key, std::string value) {
kv[std::move(key)] = std::move(value);
}
};
void demoConfig() {
Config cfg({{"mode","dev"},{"port","8080"}});
const Config& r = cfg;
// r.set("mode","prod"); // ОШИБКА: у const-ссылки нет set
auto mode = r.get("mode"); // Только чтение — так и задумывалось
cfg.set("mode","prod"); // Изменять может только неконстантный объект
}
Частые ошибки и лучшие практики
- Не добавляйте const к возвращаемому по значению типу без необходимости: это не усиливает защиту и мешает перемещению/присваиванию у временных.
- Предпочитайте const T& в параметрах для тяжёлых объектов; для мелких — значение.
- Помечайте методы как const, если они логически не меняют состояние — это документирует намерение и раскрывает больше оптимизаций.
- Используйте cbegin/cend и const-итераторы для обхода «только чтение».
- Не злоупотребляйте mutable и const_cast — они для редких случаев (кэш, внутриклассная синхронизация).
- Проектируйте API «по умолчанию неизменяемыми»: чем меньше точек изменения, тем проще тестировать и сопровождать.
Что дальше изучить
Константность тесно связана с правилами владения и временем жизни. Освоив const correctness, вы легче разберётесь с RAII, правилами трёх/пяти/нуля и безопасными абстракциями. Хотите структурированную практику, домашние задания и обратную связь? Попробуйте интенсив: Записаться на бесплатный модуль «Программирование на C++ с Нуля до Гуру» — отличный старт, чтобы уверенно применять const в реальном коде.
-
Создано 24.10.2025 17:01:37
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.