2016-05-03 14 views
5

Załóżmy, że mamy klasę "Zwierzę" i podklasy "Kot" i "Pies".Dlaczego musimy deklarować wirtualne metody jako takie

Załóżmy, że chcemy pozwolić, aby zarówno "Kot", jak i "Pies" wydały dźwięk (cat: "meow" - pies: "woof"), kiedy przekazujemy ich obiekty do funkcji pośredniej dla dowolnego "Zwierzęcia".

Dlaczego musimy użyć wirtualnej metody, aby to zrobić? Czy nie możemy po prostu zrobić Animal-> makeNoise() bez zdefiniowania wirtualnej metody w "Animal"? Skoro "Kot" i "Pies" są zwierzętami, czy nie byłoby jasne, że "makeNoise()" odnosi się do Zwierzęcia, które zostało przekazane do funkcji?

Czy to tylko kwestia składni czy czegoś więcej? Jestem prawie pewien, że w Javie nie musimy tego robić.

+4

@Antonio, wirtualne klasy bazowe nie mają absolutnie nic wspólnego z metodami wirtualnymi. Jest to tylko jeden z przypadków, gdy C++ wykonuje bardzo dobrą robotę nadużywania słowa kluczowego w kilku celach. –

+0

Co jeśli miniesz szczura, który nie miał metody 'makeNoise()'. –

Odpowiedz

10

W języku Java wszystkie funkcje składowe domyślnie są virtual (oprócz static, private i final).

W C++ wszystkie funkcje składowe domyślnie nie są virtual. Zwiększanie wydajności dodaje zarówno czas pracy, jak i rozmiar obiektu, a filozofia C++ nie płaci za to, czego nie używasz. Większość moich obiektów nie jest polimorficzna, więc nie powinienem płacić za polimorfizm, chyba że go potrzebuję. Ponieważ musisz Animal::makeNoise() być virtual, musisz jawnie określić go jako taki.

+0

W Javie funkcja 'final' może być wirtualna, jeśli nie jest ostateczna w klasie bazowej. Oznacza to, że klasa może przesłonić funkcję wirtualną i uczynić ją końcową, aby zapobiec dalszym przesłonięciom, ale nadal jest wirtualna w górę drzewa hierarchii. –

+0

Należy również wspomnieć, że funkcje inne niż wirtualne zapewniają nie tylko poprawę wydajności, ale także pewien stopień bezpieczeństwa. Na przykład wywołanie funkcji innej niż wirtualna z konstruktora może być bezpieczne, natomiast wywoływanie funkcji wirtualnych z konstruktorów jest generalnie złym pomysłem (chociaż nie jest tak źle w C++, jak w Javie). –

+0

W C++ nie płacisz za to, czego nie używasz. W Javie płacisz za wszystko. –

0

W języku java używane są również metody wirtualne. Poprawia luźne połączenie oprogramowania.

Możesz na przykład użyć biblioteki i nie wiedzieć, którego zwierzę używa wewnętrznie. Jest takie zwierzę, którego nie znasz i możesz go użyć, ponieważ jest zwierzęciem. Otrzymujesz zwierzę za pomocą metody library.getAnimal. Teraz możesz użyć swojego zwierzęcia, aby nie wiedzieć, jaki hałas generuje, ponieważ muszą wdrożyć metodę makeNoise.

Edytuj: Aby odpowiedzieć na twoje pytanie, C++ chce wyraźnej deklaracji, aw java jest domyślne. więc tak, jest to swoista specyfika specyficzna dla danego języka.

+0

W Javie tego nie robisz i nie możesz tego zrobić. Nie możesz * zadeklarować * funkcji wirtualnej jako takiej, ponieważ nie ma takiego słowa kluczowego w Javie. –

+0

, więc nie można przesłonić metody nadrzędnej w java? Metody publiczne Javy są domyślnie wirtualne. Imho – ChampS

+0

Zgadza się. A ponieważ są domyślnie wirtualne, nie możesz * zadeklarować * ich jako wirtualnych ani nie musisz tego robić. Przeczytaj uważnie to pytanie. –

0

Można powiedzieć, że trzeba to zrobić, ponieważ jest to jedna z zasad języka.

Nie bez powodu jest to pomocne.

Podczas próby sprawdzenia poprawności kodu wykorzystującego zwierzę, osoba uzupełniająca wie, jakie funkcje istnieją w zwierzęciu. Możliwe jest sprawdzenie, czy kod jest poprawny bez sprawdzenia wszystkich klas wywodzących się ze zwierząt. Tak więc kod ten nie musi zależeć od wszystkich tych klas pochodnych. Jeśli otrzymasz nową klasę od Animal, ale zapomnisz wprowadzić funkcję makeNoise, która jest błędem w nowej klasie, a nie w kodzie używającym klasy bazowej Animal, a osoba uzupełniająca może wskazać ci ten błąd. Bez funkcji wirtualnej zadeklarowanej w Animal nie można by określić, czy jest to kod wywołujący czy nowa klasa, która jest w błędzie.

Kluczowym punktem jest to, że błędy te zostałyby przechwycone podczas kompilacji dla C++ z powodu jego statycznego wpisywania. Inne języki pozwalają na dynamiczne pisanie, co może trochę ułatwić, ale błędy zostaną wykryte tylko w środowisku wykonawczym.

0

W Javie wszystkie funkcje są domyślnie wirtualne. W C++ nie są, więc gdy wywołujesz funkcję inną niż wirtualna na wskaźniku danego typu, implementacja tej funkcji jest wywoływana z adresem obiektu jako this.

class Animal { 
public: 
    void sound() { std::cout << "splat\n"; } 
    virtual void appearance() { std::cout << "animaly\n"; } 
}; 

class Cat { 
public: 
    void sound() { std::cout << "meow\n"; } 
    virtual void appearance() { std::cout << "furry\n"; } 
}; 

int main() { 
    Animal a; 
    Cat c; 
    Animal* ac = new Cat; 

    a.sound(); // splat 
    a.appearance(); // animaly 

    c.sound(); // meow 
    c.appearance(); // furry 

    ac->sound(); // splat 
    ac->appearance(); // furry 
} 

ten pojawia się, gdy chcesz napisać funkcję, która uogólniony na „Animal”, zamiast wymogu konkretnych pochodzi klasy wskaźnik.

0

C++ jest przeznaczony do pracy z tak niewielkim obciążeniem, jak to możliwe, ufając programistom, aby wykonać poprawne połączenie. Zasadniczo "daje ci broń i możliwość strzelania sobie w stopę", jak lubi często mówić jeden z moich przyjaciół. Szybkość i elastyczność są najważniejsze.

Aby poprawnie spowodować prawdziwe zachowanie polimorficzne, wymaga tego C++. Jednak! Wymagane jest tylko określenie w klasie bazowej, ponieważ wszystkie pochodne klasy będą dziedziczyć funkcje wirtualnego elementu. Jeśli członek dziedziczy funkcję wirtualnego członka, dobrą praktyką jest umieszczenie "wirtualnego" w deklaracji, ale nie jest to wymagane.

ADT zwykle realizują czyste wirtualne funkcje, aby wskazać, że wyprowadzone klasy MUSZĄ zaimplementować funkcję. Takich jak:

animal makeNoise() = 0; /*Indicates this function contains no implementation. 
and must be implemented by derived classes in order to function.*/ 

Ponownie, nie jest wymagane klasy pochodne obejmują „wirtualną” w odziedziczonej elementów, o ile klasy bazowa zawiera ten.

1

Jeśli chcesz wydedukować typ zwierzęcia, a następnie wywołać make_sound(), wówczas musisz wykonać dynamiczne pochylenie obiektu zwierzęcia dla każdego dziecka zwierzęcia. Obejmuje to każdą klasę, która jest bezpośrednio lub pośrednio dzieckiem klasy Animal.

Jest to zarówno trudne do utrzymania, jak i bardzo bolesne przy każdej zmianie, np. Dodanie nowej klasy jako dziecka do klasy zwierząt.

Ponieważ filozofia C++ to efektywność, będziesz musiał poprosić kompilator, aby zapewnił ci polimorfizm w czasie wykonywania, ponieważ jest kosztowny. Jak byś to zrobił? Podając funkcję make_sound() jako wirtualną. Tworzy to tabelę vtable (tabela wskaźników funkcji), która odnosi się do adresu make_sound(), który różni się w zależności od typu obiektu.

Nie ma potrzeby odrzucania, ponieważ pośrednictwo obsługuje wszystko dla Ciebie. To, co może być setkami linii kodu, to tylko jedna linia kodu. To jest siła indoktrynacji !!