Продолжая что-то, изученное в ошибке C++: базовая функция защищена...
Правила указателя на член C++11 эффективно лишают ключевое слово protected
любого значения, потому что защищенные члены могут быть доступны в несвязанных классах без каких-либо вредоносных/небезопасных приведений.
А именно:
class Encapsulator
{
protected:
int i;
public:
Encapsulator(int v) : i(v) {}
};
Encapsulator f(int x) { return x + 2; }
#include <iostream>
int main(void)
{
Encapsulator e = f(7);
// forbidden: std::cout << e.i << std::endl; because i is protected
// forbidden: int Encapsulator::*pi = &Encapsulator::i; because i is protected
// forbidden: struct Gimme : Encapsulator { static int read(Encapsulator& o) { return o.i; } };
// loophole:
struct Gimme : Encapsulator { static int Encapsulator::* it() { return &Gimme::i; } };
int Encapsulator::*pi = Gimme::it();
std::cout << e.*pi << std::endl;
}
Это действительно поведение соответствует стандарту?
(Я считаю это недостатком и утверждаю, что тип &Gimme::i
на самом деле должен быть int Gimme::*
, хотя i
является членом базового класса. Но я не вижу в Стандарте ничего, что делало бы это таковым, и есть очень конкретный пример, показывающий это.)
Я понимаю, что некоторые люди могут быть удивлены тем, что третий прокомментированный подход (второй тестовый пример ideone) на самом деле терпит неудачу. Это потому, что правильный способ думать о защищенном — это не «мои производные классы имеют доступ и никто другой», а «если вы наследуете от меня, вы будете иметь доступ к этим унаследованным переменным, содержащимся в ваших экземплярах, и никто другой не будет, если вы не даруй». Например, если Button
наследует Control
, то защищенные члены Control
в экземпляре Button
доступны только для Control
и Button
и (при условии, что Button
не запрещает это) фактический динамический тип экземпляра и любые промежуточные базы.
Эта лазейка подрывает этот договор и полностью противоречит духу правила 11.4p1:
Дополнительная проверка доступа помимо описанных ранее в разделе 11 применяется, когда нестатический элемент данных или нестатическая функция-член является защищенным членом своего класса именования. Как описано ранее, доступ к защищенному элементу предоставляется, поскольку ссылка встречается у друга или члена некоторого класса
C
. Если доступ должен формировать указатель на элемент (5.3.1), описатель вложенного имени должен обозначатьC
или класс, производный отC
. Все другие обращения включают (возможно, неявное) выражение объекта. В этом случае класс выражения объекта должен бытьC
или классом, производным отC
.
Спасибо AndreyT за ссылку на http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#203, где приведены дополнительные примеры, мотивирующие изменение, и содержится призыв к рассмотрению вопроса Рабочей группой по развитию.
Также актуально: GotW 76: использование и злоупотребление правами доступа
private
участников без кастинга - person Andy Prowl   schedule 07.06.2013&A::a
. - person Ben Voigt   schedule 07.06.2013reinterpret_cast
— он позволяет нарушать правила простым в использовании, но очень очевидным способом. Я думаю, что если разработчики языка хотят оставить дверь открытой для обхода проверок доступа, например, для сериализации объектов класса, синтаксисprivate_access(
member-access-expression
)
был бы намного лучше. Но вариант использования сериализации на самом деле меня не впечатляет, поскольку объекты POD могут быть тривиально сериализованы, а объекты, не относящиеся к POD, требуют помощи дизайнера объектов. - person Ben Voigt   schedule 07.06.2013