Путаница в отношении сокрытия имени и виртуальных функций

Ссылаясь на еще один вопрос

Рассмотрим код:

class Base {
public: 
    virtual void gogo(int a){
        printf(" Base :: gogo (int) \n");
    };

    virtual void gogo(int* a){
        printf(" Base :: gogo (int*) \n");
    };
};

class Derived : public Base{
public:
    virtual void gogo(int* a){
        printf(" Derived :: gogo (int*) \n");
    };
};

int main(){

    // 1)       
    Derived * obj = new Derived ; 
    obj->gogo(7);  // this is illegal because of name hiding


    // 2)      
    Base* obj = new Derived ;
    obj->gogo(7); // this is legal
}

Для случая 2)

Вызов obj->gogo(7) разрешается во время выполнения.

поскольку obj->gogo(7) законно Кажется, это означает, что Derived содержит PTR для virtual void gogo(int a) который должен был быть скрыт.

Моя путаница заключается в том, что, поскольку скрытие имени приводит к тому, что случай 1) является недопустимым, тогда способ вызова в 2) разрешается во время выполнения

а) Содержит ли vtable из Derived указатель на gogo (int).

б) Если а) не является истиной, переходит ли разрешение вызовов для виртуальных функций в vtable базового класса.

4 ответа

Вы путаете вызовы виртуальных функций и разрешение перегрузки.

Все производные классы имеют таблицы, содержащие все виртуальные функции из базового класса и любые дополнительные собственные виртуальные функции. Это используется для разрешения вызовов во время выполнения, как в вашем случае 2).

В случае 1) вы получаете ошибку от разрешения перегрузки во время компиляции. Из-за сокрытия имени, класс Derived имеет только одну вызываемую функцию. Ваш единственный выбор - вызвать эту функцию с int*,

Вы хотите переопределить перегруженную функцию, но правила скрытия не работают так.

"Правило сокрытия гласит, что сущность во внутренней области видимости скрывает вещи с одинаковыми именами во внешней области".

Обратите внимание, что не имеет значения, что он имеет другую подпись, т.е. gogo(int* a) скроет все gogo(whatever) функции с базы. Переопределение происходит только тогда, когда ваши функции имеют одинаковые имена, одинаковые подписи и виртуальные (то есть, только gogo(int* a) будет переопределено).

В книге часто задаваемых вопросов C++ предлагается использовать "не виртуальные перегрузки, которые вызывают не перегруженные виртуалы". (глава 29.05). В основном вы создаете не виртуальные перегруженные функции в базовом классе:

gogo(int a) and gogo(int* a)

которые будут вызывать виртуальные функции соответственно:

виртуальный gogo_i(int * a) и виртуальный gogo_pi(int* a)

И переопределить это виртуалы в производном классе.

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

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

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

Вы можете изменить это поведение, добавив "using Base::gogo; " в определение производного класса. Я надеюсь, что это объясняет.

Так как вы объявили второй obj как Base*vtable предоставляет все методы Base, Хотя для виртуальных методов, которые были переопределены DerivedВызываются переопределенные версии, другие методы (или перегрузки методов) остаются теми, которые были объявлены в Base,

Однако, если вы объявили указатель как Derived*Vtable даст ему методы Derivedскрывая те, которые имели одно и то же имя в Base, Следовательно, obj->gogo(7); не будет работать. Точно так же это также незаконно:

Base* obj = new Derived();

// legal, since obj is a pointer to Base, it contains the gogo(int) method.
obj->gogo(7); 

// illegal, it has been cast into a pointer to Derived. gogo(int) is hidden.
(reinterpret_cast<Derived*>(obj))->gogo(7);

Это законно:

Derived* obj = new Derived ;
obj->Base::gogo(7); // legal.

Смотрите здесь.

Другие вопросы по тегам