Изменение режима доступа к функциям в производном классе
Рассмотрим следующий фрагмент:
struct Base
{
virtual ~Base() {}
virtual void Foo() const = 0; // Public
};
class Child : public Base
{
virtual void Foo() const {} // Private
};
int main()
{
Child child;
child.Foo(); // Won't work. Foo is private in this context.
static_cast<Base&> (child).Foo(); // Okay. Foo is public in this context.
}
Является ли это законным С++? "This" изменяет режим доступа к виртуальной функции в производном классе.
Ответы
Ответ 1
Да, изменение режима доступа в производных классах является законным.
Это похожее в форме, но другое в намерении не виртуальный интерфейс идиома. Некоторое обоснование дано здесь:
Дело в том, что существуют виртуальные функции для настройки; если они также не должны быть вызваны непосредственно из кода производных классов, нет необходимости когда-либо делать что-либо, кроме частных.
Что касается того, почему вы на самом деле делаете что-то public
в базе, но private
в производном без private
или protected
наследование выходит за рамки меня.
Ответ 2
Это законный С++, §11.6/1 говорит:
Доступ проверяется в точке вызова используя тип используемого выражения для обозначения объекта, для которого функция-член называется (B * в пример выше). Доступ к член в классе, в котором он был определен (D в примере выше), вообще не известно.
Как вы заметили, Child::Foo()
, таким образом, все еще доступен через базовый класс, который в большинстве случаев нежелателен:
Child* c = new Child;
Base* b = c;
c->Foo(); // doesn't work, Child::Foo() is private
b->Foo(); // works, calls Child::Foo()
В основном, декларация, на которую вы ссылаетесь в выражении, определяет режим доступа, но виртуальные функции подрывают это как другую функцию, тогда именованный может быть фактически вызван.
Ответ 3
Это совершенно законный С++. Вы просто определяете новый метод в классе Child.
Теперь он делает то, что вы хотите, чтобы другой вопрос.
Я считаю, что режим доступа не является частью сигнатуры метода, а это значит, что вызов виртуального метода Base Foo в конечном итоге вызывает метод Child Foo.
Итак, вот вывод: это легальный С++, и он работает так, как вы ожидали.
Я не принимаю во внимание строку child.Foo();
, которая не может работать, потому что нет сомнений в том, что она пытается получить доступ к методу Child private Foo().
Ответ 4
Кажется, компилируется и вызывается правильный метод.
Помните, что спецификаторы доступа предназначены для помощи дисциплинированному программисту, а не для предотвращения любых попыток обойти его любой ценой.
В этом конкретном случае у ребенка нет бизнеса, делающего переопределенную виртуальную функцию закрытой: не предполагается ли реализовать публичный интерфейс базы, поэтому имеет место отношение "is-a"? (Если вы не использовали публичное наследование, что означает "Ребенок - это база", трюк не будет работать.)