Объекты классов также могут представлять константы:
#include <iostream> class Person { public: std::string name; unsigned age; Person(std::string p_name, unsigned p_age) { name = p_name; age = p_age; } }; int main() { const Person tom{"Tom", 38}; // мы можем получить данные константы std::cout << "Name: " << tom.name << "\tAge: " << tom.age << std::endl; // но изменить их нельзя // tom.name = "Tom"; // ! Ошибка // tom.age = 38; // ! Ошибка }
Но при работе с константными объектами мы можем получить данные их полей, но изменить их не можем. Так, если в примере выше мы раскомментируем строку
tom.name = "Tom"; // ! Ошибка
То мы столкнемся с ошибкой на этапе компиляции, так как объект tom - константа.
Константность объекта накладывает некоторые ограничения на вызов его функций. Например, в класс Person выше добавим функцию print()
для вывода данных объекта:
#include <iostream> class Person { private: std::string name; unsigned age; public: Person(std::string p_name, unsigned p_age) { name = p_name; age = p_age; } void print() { std::cout << "Name: " << name << "\tAge: " << age << std::endl; } }; int main() { const Person tom{"Tom", 38}; tom.print(); // ! Ошибка }
Как ни странно, данный пример не скомпилируется из-за функции print, хотя в ней нет никакого изменения полей объекта. Потому что в любой функции класса теоретически можно изменять его поля, а компилятор не может определить, меняется ли значение в функции или нет. Поэтому одинаково отказывается компилировать и те функции, которые меняют состояние объекта, и те функции, которые его не меняют.
Для константного объекта можно вызывать только константные функции. Для определения таких функций после списка параметров ставится ключевое слово const:
#include <iostream> class Person { private: std::string name; unsigned age; public: Person(std::string p_name, unsigned p_age) { name = p_name; age = p_age; } // константная функция void print() const { std::cout << "Name: " << name << "\tAge: " << age << std::endl; } }; int main() { const Person tom{"Tom", 38}; tom.print(); // Name: Tom Age: 38 Person bob{"Bob", 42}; bob.print(); // Name: Bob Age: 42 }
В данном случае функция print определена как константная, поэтому ее можно вызвать как для константого, так и для неконстантного объекта. В любом случае в константной функции НЕ должно происходить изменение полей класса.
Еще одно ограничение, с которым можно столкнуться, касается вызова в константной функции других функций этого же класса - константная функция может вызыть только константные функции класса:
#include <iostream> class Person { private: std::string name; unsigned age; public: Person(std::string p_name, unsigned p_age) { name = p_name; age = p_age; } std::string getName() const { return name; } unsigned getAge() const { return age; } void print() const { // в константной функции можно вызывать только константные функции std::cout << "Name: " << getName() << "\tAge: " << getAge() << std::endl; } }; int main() { const Person tom{"Tom", 38}; tom.print(); // Name: Tom Age: 38 Person bob{"Bob", 42}; bob.print(); // Name: Bob Age: 42 }
Здесь дополнительно определены функции getName и getAge, которые соответственно возвращают имя и возраст. Обе эти функции константные, поэтому их можно вызвать в константной функции print.
Еще одно ограничение, связанное с константными функциями, состоит в том, что, если мы хотим возвратить из константной функции указатель или ссылку, то они указетель должен указывать на константу, а ссылка должна быть константной. В чем это проявляется? Попробуем возвратить ссылку и указатель:
#include <iostream> class Person { private: std::string name; unsigned age; public: Person(std::string p_name, unsigned p_age) { name = p_name; age = p_age; } // возвращаем константную ссылку const std::string& getName() const { return name; } // возвращаем указатель на константу const unsigned* getAge() const { return &age; } void print() const { std::cout << "Name: " << name << "\tAge: " << age << std::endl; } }; int main() { const Person tom{"Tom", 38}; std::string tom_name =tom.getName(); const unsigned* tom_age = tom.getAge(); std::cout << "Name: " << tom_name << "\tAge: " << *tom_age << std::endl; }
Здесь константная функция getName
возвращает константную ссылку, а функция getAge
- указатель на константу.
Иногда бывает необходимо, чтобы какие-то данные константного объекта все-таки можно было менять. В этом случае для переменной, которую необходимо менять, можно использовать ключевое слово mutable. И даже если объект является константным, значение такой переменной можно изменить.
#include <iostream> class Person { public: std::string name; mutable unsigned age; // переменную age можно изменить Person(std::string p_name, unsigned p_age) { name = p_name; age = p_age; } void print() const { std::cout << "Name: " << name << "\tAge: " << age << std::endl; } }; int main() { const Person tom{"Tom", 38}; tom.age = 22; tom.print(); // Name: Tom Age: 22 }