Управление доступом в базовых и производных классах

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

Уровень доступа и спецификатор protected

Если переменные или функции в базовом классе являются закрытыми, то есть объявлены со спецификатором private то, производный класс хотя и наследует эти переменные и функции, но не может к ним обращаться. К примеру, попробуем определить в производном классе функцию, которая выводит значения приватных переменных базового класса:

#include <iostream>

class Person
{
public:
    Person(std::string name, unsigned age)
    {
        this->name = name;
        this->age = age;
    }
    void print() const
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
private:		// закрытые переменные - доступ из производного класса недоступен
    std::string name;       //  имя
    unsigned age;           // возраст
};

class Employee: public Person
{
public:
    Employee(std::string name, unsigned age, std::string company): Person(name, age)
    {
        this->company = company;
    }
    void printEmployee() const
    {
        std::cout << name << " works in " << company << std::endl;	// ! Ошибка
    }
private:
    std::string company;
};

В базовом классе Person определены приватные переменные name и age. В производном классе Employee в функции printEmployee мы пытаемся обратиться к ним, чтобы вывести их значение на консоль. И в данном случае мы столнемся с ошибкой, так как переменные name и age - приватные переменные базового класса Person. И производный класс Employee к ним не имеет доступа.

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

Например, определим переменную name со спецификатором protected:

#include <iostream>

class Person
{
public:
    Person(std::string name, unsigned age)
    {
        this->name = name;
        this->age = age;
    }
    void print() const
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
protected:
    std::string name;   // доступно из производных классов
private:
    unsigned age;
};
class Employee: public Person
{
public:
    Employee(std::string name, unsigned age, std::string company): Person(name, age)
    {
        this->company = company;
    }
    void printEmployee() const
    {
        std::cout << name << " works in " << company << std::endl;
    }
private:
    std::string company;    // компания
};

int main()
{
    Person person {"Tom", 38};
    person.print();     // Name: Tom       Age: 38

    Employee employee {"Bob", 42, "Microsoft"};
    employee.printEmployee();   // Bob works in Microsoft
}

Таким образом, мы можем использовать переменную name в производном классе, например, в методе printEmployee, но извне базового и производного классов мы к ней обратиться по-прежнему не можем.

Уровень доступа членов базового класса

Как мы увидели, спецификатор доступа - public, private, protected играют большую роль в том, к каким именно переменным и функциям базового класса могут обращаться производные классы. Однако на доступ также влияет спецификатор доступа базового класса, применяемый при установке наследования:

class Employee: public Person

Так, в примере выше мы используем спецификатор public. И здесь мы также можем использовать три варианта: public, protected или private. Если спецификатор базового класса явным образом не указан:

class Employee: public Person

то по умолчанию применяется спецификатор private (При наследовании структур, если спецификатор доступа не укзаан, то по умолчанию применяется спецификатор public).

Таким образом, в базовом классе при определении переменных и функций мы можем использовать три спецификатора для управления доступом: public, protected или private. И те же три спецификатора мы можем использовать при установке наследования от базового класса. Эти спецификаторы накладываются друг на друга и образуют 9 возможных комбинаций.

Управление доступом при наследовании классов в языке C++

Если члены базового класса определены со спецификатором private, то в производном классе они в принципе недоступны независимо от спецификатора доступа к базовому классу.

Если спецификатор базового класса - public, то уровень доступа унаследованных членов остается неизменным. Таким образом, унаследованные открытые члены являются общедоступными, а унаследованные члены со спецификатором protected сохраняют этот спецификатор и в производном классе.

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

Если спецификатор базового класса - private, то все унаследованные члены со спецификатором protected и public в производном классе наследуются как private. Они доступны в любой функции производного класса, но вне производного класса (в том числе у его наследников) они не доступны.

Рассмотрим пример. Пусть спецификатором базового класса будет private

#include <iostream>

class Person
{
public:
    Person(std::string name, unsigned age)
    {
        this->name = name;
        this->age = age;
    }
    void print() const
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
protected:
    std::string name;   // доступно из производных классов
private:
    unsigned age;
};
class Employee: private Person
{
public:
    Employee(std::string name, unsigned age, std::string company): Person(name, age)
    {
        this->company = company;
    }
    void printEmployee() const
    {
		print();    // функция print внутри класса Employee доступна
        std::cout << name << " works in " << company << std::endl;
    }
private:
    std::string company;    // компания
};

int main()
{
    Employee employee {"Bob", 42, "Microsoft"};
    employee.printEmployee();   // Bob works in Microsoft
    // employee.print();       // функция print недоступна
}

Поскольку спецификатор базового класса Person - private, то класс Employee наследует переменную name и функцию print как private-члены. К таким переменным и функциям можно обратиться внутри класса Employee. Однако вне класса Employee они будут недоступны:

Employee employee {"Bob", 42, "Microsoft"};
// employee.print();       // функция print недоступна

А если мы создадим новый класс и унаследуем его от Employee, например, класс Manager:

class Manager: public Employee
{
public:
    Manager(std::string name, unsigned age, std::string company): Employee(name, age, company)
    { }
};

То приватные переменная name и функция print из Employee в классе Manager будут недоступны.

Установка публичного доступа

Что делать, если в примере выше для класса Employee мы все таки хотим вызвать функцию print? Мы можем восстановить уровень доступа с помощью ключевого слова using:

#include <iostream>

class Person
{
public:
    Person(std::string name, unsigned age)
    {
        this->name = name;
        this->age = age;
    }
    void print() const
    {
        std::cout << "Name: " << name << "\tAge: " << age << std::endl;
    }
protected:
    std::string name;   // доступно из производных классов
private:
    unsigned age;
};
class Employee: private Person
{
public:
    Employee(std::string name, unsigned age, std::string company): Person(name, age)
    {
        this->company = company;
    }
    using Person::print;
    void printEmployee() const
    {
        std::cout << name << " works in " << company << std::endl;
    }
private:
    std::string company;    // компания
};

int main()
{
    Employee employee {"Bob", 42, "Microsoft"};
    employee.print();  // Name: Bob       Age: 42 - функция доступна
}

В классе Employee мы устанавливаем уровень доступ к функции print базового класса Person как public:

using Person::print;

После этого функция print будет иметь свой первоначальный спецификатор доступа - public и будет доступна вне класса Employee:

Employee employee {"Bob", 42, "Microsoft"};
employee.print();    // Name: Bob       Age: 42 - функция доступна

Подобным образом можно сделать публичной и переменную name, несмотря на то, что в базовом классе Person она определена как protected:

using Person::name;

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

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