Объекты классов также могут представлять константы:
#include <iostream>
#include <string>
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>
#include <string>
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>
#include <string>
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>
#include <string>
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>
#include <string>
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>
#include <string>
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
}