Модификатор const

Существует множество способов использования const в C++, которые могут здорово облегчить разработку, позволить создавать более безопасный и быстрый код. Однако практика показывает, что многие C++ программисты либо недооценивают пользу ключевого слова const в C++, либо просто плохо с ним знакомы.

На мой взгляд этот модификатор может быть очень и очень полезен при разработке программ, а потому стоит уделить ему немного времени.

Безусловно, статья в первую очередь ориентирована на новичков, но я надеюсь что она поможет освежить или уточнить знания тем кто давно сделал свой выбор в пользу C++

Давайте выясним как работает const в C++ и для чего может быть полезен.

Существует несколько способов использования const, начнем с простого и пойдем дальше.

Данные и константы

Создание переменной i типа int, значение которой нельзя изменить:

const int i = 2;
const int i(2);
int consts i = 2;
int consts i(2);

Если объявленную одним из вышеперечисленных способов переменную i попробовать изменить (т.е. написать что-то вроде i = 3;), программа не скомпилируется.

Использовать const таким образом можно вместо директивы препроцессора #define, для определения размерности массива и т.д.

Указатели

Использование const с указателями несколько сложнее, так приходится познакомиться с его новым свойством - этот модификатор может защищать как сам указатель, так и данные, на которые последний указывает.

Изменяемые (перенаправляемые) указатели на неизменяемые символы:

const char *buff = "b";
char const *buff = "b";

После такого объявления можно изменить (перенаправить) указатель, но нельзя изменить данные, на которые он указывает:

buff = "a";	//	ОК
buff = 'a';	//	Не скомпилируется, и правильно сделает

Неизменяемый указатель на символы, которые можно изменить:

char * const buff;

После такого объявления уже нельзя изменить (перенаправить) указатель, но можно поменять данные, на которые он указывает:

buff = "a";	//	Не скомпилируется
buff = 'a';	//	ОК

Общее правило таково - если const расположен слева от знака *, то он защищает данные, иначе - указатель на них.

Использовать const таким образом можно для контроля за значением указателей и данных, на которые они указывают.

Ссылки

Ссылка, через которую нельзя поменять значение:

int a = 2;
const int &iBuff = a;
int const &iBuff = a;
int & const iBuff = a;

В данном примере я намерянно установил значения, т.к. без этого объявленная ссылка не имеет смысла, ведь через нее нельзя менять значения.

const int &iBuff;	//	Редкая фигня. Если вы не понимаете почему так — стоит разобраться прежде, чем идти дальше.

Параметры

Символы буфера не могут быть изменен внутри функции:

void SomeFunction(const char *buff);	// или (char const *buff)
void SomeFunction(const char &buff);	// или (char const &buff)

Обратите внимание, что правило (для указателей) о расположении const слева или справа от знака * здесь также работает:

void SomeFunction(char & const buff);	// Защита копии указателя. Бессмысленно.

Такое использование модификатора очень полезно, оно позволяет защищать данные, передаваемые в качестве параметров, от изменения внутри функции.

Данные класса

static const позволяет инициализировать значения членов класса прямо в его объявлении:

class cClass
{
	private:
		static const int i = 2;	//	Инициализировали значение прямо в объявлении класса
 
	public:
		cClass() {};
		~cClass() {};
		int GetI() { return i; };
};

Методы класса

Методы класса, если объявить их как const, будут считать константой this. Это значит что они не смогут менять значения переменных класса.

class cClass
{
	private:
		int i;
 
	public:
		cClass(int a) {}
 
		void Function() const
		{
			i = 5;	//	Не работает, т.к. this считается константой
		}
};

Вы можете создавать константные методы класса, чтобы уберечь переменные класса от изменения его методами, но имейте в виду что конструктор и деструктор нельзя сделать константными.

Перегрузка методов класса

Напишем класс с двумя одноименными методами, один из которых сделаем const:

class cClass
{
	private:
		int i;
 
	public:
 
		cClass(int a)
		{
			i = a;
			cout << "Constructor makes x = " << i << endl;
		}
 
		void Function()
		{
			cout << "f() tell x = " << i << endl;
		}
 
		void Function() const
		{
			cout << "f() const tell x = " << i << endl;
		}
};

Теперь создадим 2 экземпляра класса — Class и Class2, при этом Class2 сделаем const:

int main()
{
	cClass Class(3);
	Class.Function();
 
	cClass const Class2(3);
	Class2.Function();
 
	char gg[20];
	scanf(gg, 20);
 
	return 0;
}

Запустив программу, посмотрим какой метод будет вызваться для каждого из них:

Constructor makes x = 3
f() tell x = 3
Constructor makes x = 3
f() const tell x = 3

Очевидно что константный объект вызвал константный же метод, а наоборот - обычный объект вызвал обычным метод. Т.о. const можно использовать для перегрузки методов класса, необходимо только учитывать — при отсутствии не-const методов, по умолчанию будут использоваться const-методы.

Константный класс
Говоря "константный класс" подразумевают в первую очередь объявление вида:

const cClass Class;

Созданный таким образом объект будет не в состоянии изменить состояние класса и сможет вызывать только свои константные методы. Также следует учесть, что при таком создании все данные класса будут считаться const, независимо от того, как они были объявлены.

  • Категории:

Комментарии

Добавить комментарий

Адрес показан не будет
CAPTCHA
Антибот
Как оформить комментарий?