Zastępowanie metod niewirtualnych

84

Załóżmy taki scenariusz w Visual C ++ 2010:

#include <iostream>
#include <conio.h>

using namespace std;

class Base
{
public:
    int b;
    void Display()
    {
        cout<<"Base: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Base: Virtual display."<<endl;
    };
};

class Derived : public Base
{
public:
    int d;
    void Display()
    {
        cout<<"Derived: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Derived: Virtual display."<<endl;
    };
};

int main()
{
    Base ba;
    Derived de;

    ba.Display();
    ba.vDisplay();
    de.Display();
    de.vDisplay();

    _getch();
    return 0;
};

Teoretycznie wynik tej małej aplikacji powinien wyglądać następująco:

  • Podstawa: wyświetlacz niewirtualny.
  • Podstawa: wirtualny wyświetlacz.
  • Podstawa: wyświetlacz niewirtualny.
  • Pochodny: wyświetlacz wirtualny.

ponieważ metoda Display klasy Base nie jest metodą wirtualną, więc klasa pochodna nie powinna mieć możliwości jej przesłonięcia. Dobrze?

Problem w tym, że kiedy uruchamiam aplikację, wypisuje to:

  • Podstawa: wyświetlacz niewirtualny.
  • Podstawa: wirtualny wyświetlacz.
  • Pochodny: wyświetlacz niewirtualny.
  • Pochodny: wyświetlacz wirtualny.

Więc albo nie rozumiałem pojęcia metod wirtualnych, albo coś dziwnego dzieje się w Visual C ++.

Czy ktoś mógłby mi pomóc w wyjaśnieniu?

Leif Lazar
źródło
absolutnie miałbyś Base: Non-virtual display. przy zmianie linii na de.Base::Display().
v.oddou,

Odpowiedzi:

128

Tak, trochę nie rozumiesz.

Metoda o tej samej nazwie w klasie pochodnej spowoduje ukrycie metody nadrzędnej w tym przypadku. Można by sobie wyobrazić, że gdyby tak nie było, próba utworzenia metody o takiej samej nazwie jak metoda niewirtualna klasy bazowej powinna spowodować błąd. Jest to dozwolone i nie stanowi problemu - a jeśli wywołasz metodę bezpośrednio, tak jak to zrobiłeś, zostanie nazwana dobrze.

Ale ponieważ nie są wirtualne, mechanizmy wyszukiwania metod w C ++, które pozwalają na polimorfizm, nie będą używane. Na przykład, jeśli utworzyłeś instancję swojej klasy pochodnej, ale wywołałeś swoją metodę 'Display' przez wskaźnik do klasy bazowej, zostanie wywołana metoda bazy, podczas gdy dla 'vDisplay' zostanie wywołana metoda pochodna.

Na przykład spróbuj dodać te wiersze:

Base *b = &ba;
b->Display();
b->vDisplay();
b = &de;
b->Display();
b->vDisplay();

... i obserwuj wynik zgodnie z oczekiwaniami:

Podstawa: wyświetlacz niewirtualny.
Podstawa: wirtualny wyświetlacz.
Podstawa: wyświetlacz niewirtualny.
Pochodny: wyświetlacz wirtualny.

sje397
źródło
Cześć @ sje397, dziękuję za odpowiedź. Czy możesz napisać przykład wywołania metody, jak powiedziałeś, za pomocą wskaźnika do klasy bazowej? Dziękuję Ci!
Leif Lazar,
również, jak powiedziałem, możesz TAKŻE wywołać (niewirtualną) metodę podstawową z wyprowadzonej instancji, używając składni rozpoznawania zakresu.
v.oddou,
1
Tak więc, dla pewności, mogę zdefiniować metodę w klasie bazowej i przesłonić ją w klasie pochodnej, niezależnie od deklarowania jej jako wirtualnej, czy nie. Jedyną różnicą jest to, że jeśli wskaźnik bazowy wskazuje na obiekt klasy pochodnej, wywołanie tej metody spowoduje wywołanie metody klasy bazowej, jeśli nie jest wirtualna, i metody klasy pochodnej, jeśli jest wirtualna. Czy to prawda? Czy jest jakaś inna różnica?
SexyBeast
@Cupidvogel Tak, zgadza się. Zadeklarowanie jej jako „wirtualnej” oznacza, że ​​C ++ użyje mechanizmów do obsługi polimorfizmu i sprawdzi, czy istnieje bardziej pochodna wersja metody, gdy wywołujesz ją przez wskaźnik klasy bazowej. Nie przychodzi mi do głowy żadna inna różnica.
sje397
Czy wystarczy zmienić tylko plik nagłówkowy? A może źródło musi być skompilowane ze słowem kluczowym „virtual”?
Paul Knopf
14

Tak, trochę źle zrozumiałeś:

Czyste funkcje wirtualne:

virtual void fun1()=0 -> musi zostać przesłonięty w klasie pochodnej

Funkcje wirtualne:

virtual void fun2() -> można zmienić

Normalne funkcje:

void fun3() -> nie zastępuj go

Aby uzyskać polimorfizm w czasie wykonywania, musisz przesłonić funkcje wirtualne w języku c ++

Avinash Aitha
źródło
5

Myślę, że lepiej byłoby też spojrzeć na to w kontekście wiązania statycznego i dynamicznego.

Jeśli metoda nie jest wirtualna (jest już domyślnie w C ++ w przeciwieństwie do Javy), wówczas metoda wiąże się z obiektem wywołującym w czasie kompilacji, przez co nie można poznać rzeczywistego obiektu, który zostanie wskazany w czasie wykonywania. Zatem liczy się tylko typ zmiennej, czyli „Baza”.

stdout
źródło