Как стать автором
Обновить

В C++ единицей инкапсуляции является класс

Время на прочтение3 мин
Количество просмотров18K
Заголовок статьи на самом деле представляет собой не одно утверждение, а два, хотя оба они известны:
  1. В C++ единицей инкапсуляции является класс – а не отдельный объект ([Stroustrup3e], 24.3.7.4).
  2. В C++ единицей инкапсуляции является класс – а не класс вместе с его ниже стоящей иерархией.

Второе утверждение не является тривиальным, поскольку, например, популярно следующее толкование защищенных (protected) членов ([Stroustrup3e], 15.3):
Если член защищен, его имя может использоваться только в функциях-членах и друзьях класса, в котором он объявлен, и классов, производных от него.

На самом деле данное утверждение является, говоря математическим языком, необходимым, но не достаточным:
Для использования имени защищенного члена необходимо, но не достаточно, чтобы обращение к имени производилось из функции-члена или друга класса, в котором он объявлен, или из классов, производных от него.
Утверждение становится необходимым и достаточным только при добавлении уточнения ([Stroustrup3e], 15.3.1):
Производный класс может осуществлять доступ к защищенным членам базового класса только для объектов его собственного типа.

Для демонстрации приведем код:
class Base {
protected:
    void protected_f() {}
private:
    void private_f() {}
public:
    void base_f() {
        private_f(); //correct: *this object therefore object of own class Base
        Base b;
        b.private_f(); //correct: b is object of class Base (1)
    }
};

class Derived : public Base {
    void derived_f() {
        Derived d;
        d.protected_f(); //correct: d is object of class Derived therefore d->Derived::f() is called (2)
        Base b;
        b.protected_f(); //compiler error: Base::f() is protected (3)
        ((Derived*)(&b))->protected_f(); //dangerous non-dynamic downcast, but w/o compiler error (4)
    }
};

Первое утверждение тривиально и демонстрируется (1): this->base_f() вызывает b.private_f(), вызов корректен, потому что хотя *this и b — разные объекты, но являются объектами одного класса Base.
Второе утверждение демонстрируется (2) и (3). Вызов (2) корректен, поскольку имя protected_f унаследовано в классе Derived и this->derived_f() вызывает d.Derived::protected_f() — функцию класса Derived, а *this и d — разные объекты одного и того же класса Derived. Вызов (3) не корректен из-за попытки нарушения инкапсуляции: this->derived_f() объекта *this класса Derived пытается вызвать защищенный Base::protected_f() объекта b класса Base, то есть выйти за пределы своей единицы инкапсуляции — класса Derived в иерархию Base-Derived. this->derived_f() имеет право вызывать только те защищенные члены, которые принадлежат объектам того же класса Derived, что и сам *this, например, Derived::protected_f().
Строка (4) демонстрирует, что вторая часть утверждения о единице инкапсуляции обходится проще, чем первая: если для доступа к закрытым (private) членам из-за пределов единицы инкапсуляции приходится прибегать к трюкам типа низкоуровневого преобразования экземпляров объектов или переопределения ключевых слов, то для доступа к защищенным членам с нарушением инкапсуляции достаточно понижающего C-style cast указателей.

В заключении отметим, что на двойственность утверждения о единице инкапсуляции можно взглянуть с нескольких точек зрения:
  • инкапсуляция соблюдается как для закрытых, так и для защищенных членов;
  • единицы инкапсуляции не мельче и не крупнее класса;
  • инкапсулируются имена и унаследованные имена, а не члены.


Литература


[Stroustrup3e] Б. Страуструп. Язык программирования C++, 3-е изд./ Пер. с англ. — СПб.; М.: «Невский диалект» — «Издательство БИНОМ», 1991 г.
Теги:
Хабы:
+5
Комментарии29

Публикации

Изменить настройки темы

Истории

Работа

QT разработчик
6 вакансий
Программист C++
118 вакансий

Ближайшие события

PG Bootcamp 2024
Дата16 апреля
Время09:30 – 21:00
Место
МинскОнлайн
EvaConf 2024
Дата16 апреля
Время11:00 – 16:00
Место
МоскваОнлайн
Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн