Подтвердить что ты не робот

Открытая виртуальная функция, производная от private в С++

Я пытался выяснить, что происходит, когда производный класс объявляет виртуальную функцию частной. Ниже приведена программа, которую я написал

#include <iostream>
using namespace std;
class A
{
    public:
        virtual void func() {
        cout<<"A::func called"<<endl;
    }
    private:
};
class B:public A
{
    public:
    B()
    {
        cout<<"B constructor called"<<endl;
    }
    private:
    void func() {
        cout<<"B::func called"<<endl;
    }
};
int main()
{
    A *a = new B();
    a->func();
    return 0;
}

Удивительно (для меня) выход был:

B constructor called
B::func called

Разве это не нарушает набор личных прав для этой функции. Это ожидаемое поведение? Это стандартное обходное решение или лазейка? Уровни доступа обойдены при разрешении вызовов функций через VTABLE?

Любое понимание этого поведения было бы очень полезно.

Кроме того, было упомянуто, что частное переопределение виртуального члена помешает другим классам наследовать его. Даже у этого есть проблемы. Изменение указанной программы включает:

class C: public B
{
    public:
    void func() {
        cout<<"C::func called"<<endl;
    }
};

и основной тестовой программы:

int main()
{
    A *a = new C();
    a->func();
    return 0;
}

:

C::func called
4b9b3361

Ответ 1

Поведение правильное. Всякий раз, когда вы объявляете свою функцию "виртуальной", вы инструктируете компилятор генерировать виртуальный вызов вместо прямого вызова этой функции. Всякий раз, когда вы переопределяете виртуальную функцию в классе потомков, вы определяете поведение этой функции (вы не меняете режим доступа для тех клиентов, которые полагаются на интерфейс "родителя" ).

Изменение режима доступа для виртуальной функции в классе потомков означает, что вы хотите скрыть ее от тех клиентов, которые напрямую используют класс потомков (которые полагаются на "дочерний" интерфейс).

Рассмотрим пример:

void process(const A* object) {
   object->func();
}
Функция

"process" зависит от родительского интерфейса. Ожидается, что он будет работать для любого класса, открытого из A. Вы не можете публиковать B из A (говоря "каждый B - это A" ), но скрыть часть его интерфейса. Те, кто ожидают "А", должны получить полностью функциональный "А".

Ответ 2

Это четко определенное поведение. Если a были B*, это не скомпилировалось бы. Причина в том, что доступ к члену статически ставится компилятором, а не динамически во время выполнения. Многие книги на С++ предполагают, что вы избегаете кодирования, потому что это путает менее опытных кодеров.

Ответ 3

Ну, вы вызываете A::func(), который public, хотя в объекте B он переопределяется B::func(). Это общая картина со следующими последствиями:

  • func не предназначен для вызова на производных объектах B

  • func не может быть переопределен в классах, полученных из B