C++ Модификаторы доступа класса

Классы & объекты C++

В объектно-ориентированном программировании C++ (ООП) инкапсуляция данных — одна из ключевых концепций. Проще говоря, она означает "скрытие" данных и предоставление внешнему миру только необходимых интерфейсов для обеспечения безопасности данных.

Чтобы контролировать, кто может видеть эти данные, C++ предоставляет три ключевых слова, называемыемодификаторами доступа:

  • public(public)
  • private(private)
  • protected(protected)
class Base { public: //Открытые члены protected: // Защищённые члены private: // Приватные члены };

Для простоты понимания можно представитьclass как ваш дом:

Модификаторы Аналогия из реального мира Кто имеет доступ? Типичное использование
public Гостиная/Входная дверь Все(внутри класса, подклассы, внешний код) Интерфейсные функции (API), предоставляемые внешнему миру.
protected Спальня Вы и ваши дети(внутри класса, подклассы) Данные, используемые только внутри семьи (иерархия наследования).
private Сейф Только сам класс(только внутри самого класса) Ключевые данные, переменные, которые не следует изменять без необходимости.

Примечание: Если модификатор не указан, члены класса C++По умолчаниюprivate(private).

public-члены

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

Пример

#include <iostream> using namespace std; class Line { public: double length; void setLength( double len ); double getLength( void ); }; // Определение функции-члена double Line::getLength(void) { return length ; } void Line::setLength( double len ) { length = len; } // Главная функция программы int main( ) { Line line; // Задать длину line.setLength(6.0); cout << "Длина линии : " << line.getLength() <<endl; // Задать длину без использования функции-члена line.length = 10.0; // OK: потому что length — public cout << "Длина линии : " << line.length <<endl; return 0; }

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

Length of line : 6
Length of line : 10

закрытые члены

Закрытые члены полностью изолированы от внешней части класса; внешний код не может читать, изменять или вызывать их, а производные классы также не имеют прямых прав доступа. Только собственные функции-члены класса и сущности, получившие разрешение friend, могут работать с этим содержимым.

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

Пример

class Box { double width; public: double length; void setWidth( double wid ); double getWidth( void ); };

На практике мы обычно определяем данные в разделе private, а связанные функции — в разделе public, чтобы эти функции можно было вызывать и вне класса, как показано ниже:

Пример

#include <iostream> using namespace std; class Box { public: double length; void setWidth( double wid ); double getWidth( void ); private: double width; }; // Определение функции-члена double Box::getWidth(void) { return width ; } void Box::setWidth( double wid ) { width = wid; } // Главная функция программы int main( ) { Box box; // Установить length без использования функции-члена box.length = 10.0; // OK: потому что length — public cout << "Длина коробки : " << box.length <<endl; // Задать ширину без использования функции-члена // box.width = 10.0; // Ошибка: потому что width является private box.setWidth(10.0); // Задать ширину с помощью функции-члена cout << "Ширина box : " << box.getWidth() <<endl; return 0; }

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

Length of box : 10
Width of box : 10

protected-члены

protectedСуществование ... в основном дляНаследование。

  • Если нет наследования, то оно иprivateто же самое (не видно посторонним).
  • Если есть наследование,Подкласс (производный класс) может получить доступ кprotectedчлену родительского класса, но не может получить доступ кprivateчленам.

В следующей главе вы узнаете о производных классах и наследовании. Теперь вы можете увидеть в примере ниже: мы от родительского классаBoxсоздали подклассsmallBox。

Пример ниже похож на предыдущий пример; здесьwidthчлены могут быть доступны любой функции-члену производного класса smallBox.

Пример

#include <iostream> using namespace std; class Box { protected: double width; }; class SmallBox:Box // SmallBox — производный класс { public: void setSmallWidth( double wid ); double getSmallWidth( void ); }; // Функция-член производного класса double SmallBox::getSmallWidth(void) { return width ; } void SmallBox::setSmallWidth( double wid ) { width = wid; } // Главная функция программы int main( ) { SmallBox box; // Задать ширину с помощью функции-члена box.setSmallWidth(5.0); cout << "Ширина коробки : "<< box.getSmallWidth() << endl; return 0; }

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

Width of box : 5

Изменение прав доступа при наследовании

Когда вы определяете подкласс (например,class B : public A), после двоеточияpublicтакже является модификатором доступа; он определяеткак члены родительского класса "отображаются" в дочернем классе。

Это выглядит сложно, но нужно запомнить только этопринцип понижения:

Способ наследования определяет наивысший уровень доступа, который члены родительского класса имеют в дочернем классе.
Если способ наследования строже, чем исходный уровень доступа члена, уровень доступа члена "понижается" до уровня способа наследования.

Таблица изменения прав доступа

исходные права доступа членов базового класса public-наследование(наиболее часто используется) protected-наследование private-наследование
public остаются public понижены до protected понижен до private
protected остаются protected остаются protected понижен до private
private Недоступно Недоступно Недоступно

Примечание: «Недоступно» означает, что код дочернего класса не может напрямую использовать переменную, но переменная всё ещё существует в памяти.

исчерпывающий демонстрационный код

Для наглядности мы переименовываем переменную вpub_var(public),pro_var(protected),pri_var(private).

Пример

#include <iostream>
using namespace std;

class Parent {
public:
    int pub_var;
protected:
    int pro_var;
private:
    int pri_var; // Доступ имеет только сам Parent

public:
    Parent() { pub_var = 1; pro_var = 2; pri_var = 3; }
};

// 1. Открытое наследование — наиболее распространённое, соответствует оригиналу
class ChildA : public Parent {
public:
    void test() {
        cout << pub_var << endl; // ОК
        cout << pro_var << endl; // ОК
        // cout << pri_var << endl; // Ошибка! Приватные члены класса Parent не видны
    }
};

// 2. Защищённое наследование — всё становится защищённым
class ChildB : protected Parent {
public:
    void test() {
        cout << pub_var << endl; // ОК, но с точки зрения ChildB он защищён
        cout << pro_var << endl; // ОК
    }
};

// 3. Закрытое наследование — всё становится закрытым
class ChildC : private Parent {
public:
    void test() {
        cout << pub_var << endl; // OK, но с точки зрения ChildC это private
        cout << pro_var << endl; // OK, но с точки зрения ChildC это private
    }
};

int main() {
    ChildA a;
    cout << a.pub_var << endl; // OK, доступен извне
    // cout << a.pro_var << endl; // Ошибка, protected недоступен извне

    ChildB b;
    // cout << b.pub_var << endl; // Ошибка! Поскольку это protected-наследование, pub_var становится protected извне

    ChildC c;
    // cout << c.pub_var << endl; // Ошибка! Поскольку это private-наследование, pub_var становится private извне

    return 0;
}

Итог

  • Публичный: Любой может использовать его. Используется как интерфейсы (API).
  • Private: Только сам класс может использовать его. Используется для хранения данных (безопасный вариант по умолчанию).
  • Protected: Только семейство (цепочка наследования) может использовать его. Используется, чтобы оставить лазейку для дочерних классов.

Наследование:

  • Открытое наследование: Атрибуты родительского класса остаются без изменений (наиболее часто используется).
  • Закрытое/защищённое наследование: Ужесточает права доступа членов родительского класса, обычно используется в особых сценариях реализации.

Классы & объекты C++

Другие расширения