Константные объекты и функции

Последнее обновление: 10.03.2023

Константные объекты

Объекты классов также могут представлять константы:

#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

Иногда бывает необходимо, чтобы какие-то данные константного объекта все-таки можно было менять. В этом случае для переменной, которую необходимо менять, можно использовать ключевое слово 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
}
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850