Как определить, какая функция будет вызываться, если она виртуальная, а когда нет? - PullRequest
3 голосов
/ 29 мая 2020

Посмотрите этот отрывок из программы.

Я вижу, что вызов cout << obj->foo(); не является полиморфным c. На самом деле, это очевидно, потому что у него нет спецификатора virtual.

Но меня смущает cout << ((B*)obj)->foo(); Почему программа не использует определение виртуальной функции B и будет вызывать третью версия foo()?

 #include <iostream> 
using namespace std; 

class A{ 
public:     
    int foo(){ return 1; } 
}; 
class B: public A{ 
public:     
    virtual int foo(){ return 2; } 
}; 
class C: public B{ 
public:     
    int foo(){ return 3; } 
}; 

int main() {  
    A* obj = new C; 

    cout << obj->foo(); 
    cout << ((B*)obj)->foo(); 
    cout << ((C*)obj)->foo(); 

    return 0;  
}

Ответы [ 3 ]

6 голосов
/ 29 мая 2020

A::foo() не virtual. Вызов foo() через указатель A* (или A& ссылку) вызовет A::foo() напрямую без каких-либо полиморфных c диспетчеризации.

B::foo() равно virtual. Вызов foo() через указатель B* (или ссылку B&) отправит вызов наиболее производной реализации foo(), существующей в объекте, на который ссылается B* (или B&).

C происходит от B, а C::foo() переопределяет B::foo(), а obj указывает на объект C, поэтому C::foo() вызывается polymorphi c dispatch, когда foo() вызывается через указатель B* или C* (или ссылку B& или C&).

4 голосов
/ 29 мая 2020

Потому что ((B*)obj)->foo(); изначально ведет себя как B* b = (B*)obj; b->foo() и вызывает C::foo. Вы можете явно вызвать метод базы, например, ((B*)obj)->B::foo();.

#include <iostream> 
using namespace std; 

class A{ 
public:     
    int foo(){ return 1; } 
}; 
class B: public A{ 
public:     
    virtual int foo(){ return 2; } 
}; 
class C: public B{ 
public:     
    int foo() override { return 3; } 
}; 

int main() {  
    A* obj = new C; 

    cout << obj->foo();
    cout << ((B*)obj)->B::foo(); 
    cout << ((C*)obj)->foo(); 

    return 0;  
}

Вывод: 123

3 голосов
/ 29 мая 2020

Функция-член foo является виртуальной от класса B вниз, т.е. также в C, даже если она не отмечена там virtual или override.

Таким образом, вызовите ((B*)obj)->foo() является виртуальным вызовом, фактически вызывающим вызов C::foo.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...