Управление доступом. Инкапсуляция

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

Класс может определять различное состояние, различные функции. Однако не всегда желательно, чтобы к некоторым компонента класса был прямой доступ извне. Для разграничения доступа к различным компонентам класса применяются спецификаторы доступа

Спецификатор public делает члены класса - поля и функции открытыми, доступными из любой части программы. Например, возьмем следующий класс Person:

#include <iostream>

class Person 
{
public:
    std::string name;
    unsigned age;
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        age = p_age;
    }
};
int main()
{
    Person tom{"Tom", 38};
    // поля name, age и функция print общедоступные
    tom.name = "Tomas"; 
    tom.age = 22;
    tom.print();    // Name: Tomas   Age: 22
}

То есть в данном случае поля name и age и функция print являются открытыми, общедоступными, и мы можем обращаться к ним во внешнем коде. Однако это имеет некоторые недостатки. ТаК, мы можем обратиться к полям класса и присвоить им любые значения, даже если они будут не совсем корректными, исходя из логики прогаммы:

Person tom("Tom", 22);
tom.name = "";
tom.age = 1001;

В том числе можно присвоить какие-то недопустимые значения. Например, полю age можно передать чересчур большой нереальвй возраст. Или мы не хотим, чтобы имени можно было присвоить пустую строку. Естественно это не очень хорошая ситуация.

Однако с помощью другого спецификатора private мы можем скрыть реализацию членов класса, то есть сделать их закрытыми, инкапсулировать внутри класса. Перепишем класс Person с применением спецификатора private:

#include <iostream>

class Person 
{
private:
    std::string name;
    unsigned age;
public:
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        age = p_age;
    }
};
int main()
{
    Person tom{"Tom", 38};
    // функция print общедоступная
    tom.print();    // Name: Tom   Age: 22

    // поля name и age вне класса недоступны
    // tom.name = "";  
    // tom.age = 1001;
}

Все компоненты, которые определяются после спецификатора private и идут до спецификатора public, являются закрытыми, приватными. Теперь теперь мы не можем обратиться к переменным name и age вне класса Person. Мы можем к ним обращаться только внутри класса Person. А функция print и конструктор по прежнему общедоступные, поэтому мы можем обращаться к ним в любом месте программы.

Стоит отметить, что в данном случае мы все равно можем передать некорректные значения - через конструктор. В этом случае можно проверять входные данные и использовать различные стратегии, например, не создавать объект или передавать ему данные по умолчанию, но в целях упрощения я опущу подобную проверку.

Если для каких-то компонентов отсутствует спецификатор доступа, то по умолчанию применяется спецификатор private. Так, предыдущий класс Person будет аналогичен следующему

class Person 
{
    std::string name;
    unsigned age;
public:
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        age = p_age;
    }
};

Опосредование доступа

Хотя в примере выше мы избегаем установки некорректных значений, тем не менее иногда может потребоваться доступ к подобным полям. Например, человек стал старше на год - надо изменить возраст. Или мы хотим отдельно получить имя. В этом случае мы можем определить специальные функции, через которые будем контроллировать доступ к состоянию класса:

#include <iostream>

class Person 
{
private:
    std::string name;
    unsigned age;
public:
    Person(std::string p_name, unsigned p_age)
    { 
        name = p_name; 
        if (p_age > 0 && p_age < 110) 
            age = p_age;
		else
			age = 18;	// если значение некорректное, устанавливаем значение по умолчанию
    }
    void print() 
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
    void setAge(unsigned p_age) 
    {
        if (p_age > 0 && p_age < 110) 
            age = p_age;
    }
    std::string getName()
    {
        return name;
    }
    unsigned getAge()
    {
        return age;
    }
};
int main()
{
    Person tom{"Tom", 38};
    // изменяем возраст
    tom.setAge(22);
    tom.setAge(123);
    tom.print();    // Name: Tom   Age: 22

    //отдельно получаем имя
    std::cout << "Person name: " << tom.getName() << std::endl;
}

Чтобы можно было получить извне значения переменных name и age, определены дополнительные функции getAge и getName. Установить значение переменной name напрямую можно только через конструктор, а значение переменной age - через конструктор или через функцию setAge. При этом функция setAge устанавливает значение для переменной age, если оно соответствует определенным условиям.

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

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850