Константные методы
Просветите, пожалуйста, на тему константных методов в C++; зачем нужны, в чем преимущества?! С небольшими примерами кода, если не затруднит.
Просветите, пожалуйста, на тему константных методов в C++; зачем нужны, в чем преимущества?! С небольшими примерами кода, если не затруднит.
Константные методы служат для определения того, что можно сделать с классом без побочного действия на его состояние, и что изменяет его состояние. Пользователю класса, в котором правильно расставлены const
, в некоторых случаях, например, удаётся избежать лишнего копирования объектов без потери данных. Также при правильном использовании const
на уровне компиляции исключается случайное изменение объекта в методе, который не должен ничего в объекте менять. Например, есть класс типа
class A
{
int a;
int b;
public:
int getA() { b = b + 1;return a; }
int getB() { a = a + 1;return b; }
};
Здесь мнемоника и семантика немного несоответствуют. Вот хочется думать, что если
A x, y;
...
bool f = x.getA() == y.getA() && x.getB() == y.getB();
f
истина, то x
и y
в чём-то похожи, но внутренности класса реализованы не так. Пользователь класса должен быть уверен, что геттер ничего не испортит. Хороший разработчик класса должен явно описывать характер поведения метода с помощью const
или отсутствия const
class A
{
int a;
int b;
public:
int getA() const { return a; }
int getB() const { return b; }
};
Но если вам вдруг понадобится делать прогу, в которой есть что-то вроде подсчёта обращений к переменной, то семантика геттеров заставляет употребить const
, но счётчик обращений нужно увеличивать. Для этого случая есть слово mutable
! Этим словом мы пометим поля, которые вроде как бы члены класса, но они не отражают что ли его состояние
class A
{
int a;
int b;
mutable int cntA;
mutable int cntB;
public:
int getA() const { cntA++; return a; }
int getB() const { cntB++; return b; }
};
Несколько слов о применении константных методов. Классы с константными методами удобно применять:
1.Когда над проектом работают несколько программистов. Когда проектируется класс разработчик сразу определяет, что и при каких условиях может меняться, а что должно остаться неизменным. Константные классы тут как раз к месту для построения всех "запретительных заборов", чтобы другой разработчик случайно не изменил состояние объекта. Как правило, классы с константными методами не имеют конструктор по умолчанию, а только конструкторы, зависящие от каких-то объектов. Это очень удобно, так как все члены будут инициализированы. Кстати, это единственный способ инициализации константных членов.
2.Использование объектов const. Если создается константный объект, то для обращения к его методам и членам надо использовать константные методы. Например, если в функцию передается указатель на объект, но хочется обезопасить его от изменения, то нужно его объявлять как const
В нестатические методы класса компилятор неявно передаёт константный указатель на объект от которого был вызван метод, его сигнатура
T* const this // адресс на который указывает this поменять нельзя
благодаря ему можно связать обращение к переменным и методам с фактическим адрессом экземпляра. Следовательно код:
void f()
{
m_a = 10;
}
эквивалентен
void f()
{
this->m_a = 10;
}
В случаи с константными методами, вы меняем два поведения:
Теперь данный метод может быть вызван для константного объекта.
const Foo a;
a->f(); // only if f() is const method
Теперь внутрь данных методов передаётся константный указатель на константу.
const T* const this; // адресс и состояние объекта на который указывает this поменять нельзя
следовательно
void f()
{
this->m_a = 10; // ошибка нельзя менять состояние константного объекта
}
Преймущества константного метода:
grammar const
(всё что должно быть const - должно иметь данный спецификатор) - если метод не меняет объект то почему бы программисту явно не указать это себе, другим программистам и компилятору?Можно перегрузить метод через его константность:
struct Array
{
int x;
int operator[]( int index ) const
{
return x;
}
int& operator[]( int index )
{
return x;
}
};
int main()
{
Array a;
a[1] = 10; // int& operator[]( int index )
// ok
const Array b;
b[1] = 10; // int operator[]( int index ) const
// error: `int` is not lvalue
}