• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Funkcje wirtualne

+1 głos
463 wizyt
pytanie zadane 29 stycznia 2019 w C i C++ przez Seqak Początkujący (420 p.)
Cześć,

Mam problem ze zrozumieniem przydatności i możliwości jakie dają funkcje wirtualne. Pewnie wynika to z faktu, że "wydaje" mi się, że rozumiem temat.

Przechodząc do rzeczy. Jeśli ktoś ma pomysł jak na najprostszych przykładach z życia wziętych, wytłumaczyć możliwości jakie dają funkcje wirtualne, będę ogromnie wdzięczny. Czemu lepiej użyć takiej metody niżeli pisania dla każdego obiektu osobnej np nazwanej tak samo lub odziedziczonej.

Ja to widzę mniej więcej tak: Po co tworzyć f. wirtualną, potem wskaźniki itp. skoro i tak każdą metodę musimy napisać jeszcze raz, ponieważ różne obiekty mogą mieć różne właściwości danej metody. Piszemy każdą osobno (bo i tak musimy to zrobić) następnie tylko używamy obiekt.metoda(); i gotowe w przypadku funkcji wirtualnych, dochodzą wskaźniki itp.

Nie zrozumcie mnie źle, ja nie próbuję negować przydatności f. wirtualnych, dlatego opisałem jak ja to widzę, żeby osoba, która to przeczyta wskazała błąd w moim rozumowaniu.
komentarz 29 stycznia 2019 przez j23 Mędrzec (169,620 p.)
W tagach masz polimorfizm, i to jest zagadnienie, z którym powinieneś się dobrze zapoznać, bo ono nadaje sens funkcjom wirtualnym.
komentarz 29 stycznia 2019 przez adrian17 Ekspert (306,660 p.)

skoro i tak każdą metodę musimy napisać jeszcze raz, ponieważ różne obiekty mogą mieć różne właściwości danej metody

Jeśli jednak klasa dziedziczy po drugiej, to założenie jest że w większości jej cechy i zachowanie jest takie samo jak w bazowej. Metody wirtualne przeładowujemy, gdy jakieś zachowania jednak się różnią - co jest mniejszością, nie większością.

2 odpowiedzi

+1 głos
odpowiedź 30 stycznia 2019 przez Patrycjerz Mędrzec (192,440 p.)
wybrane 30 stycznia 2019 przez Seqak
 
Najlepsza

Mylisz chyba pojęcia. Polimorfizm w C++ to używanie wskaźników (lub referencji) do klas bazowych w celu dostania się do metod obiektów klas pochodnych za pomocą przykrycia metod wirtualnych klasy bazowej. Te metody nie muszą być przykryte, inaczej zostanie wywołana metoda z klasy bazowej. Metody czysto wirtualne już wymagają przykrycia, a gdy nie zostanie to wykonane, takowa klasa pochodna staje się klasą abstrakcyjną, gdyż nie dochowała wymogu interfejsu.

A dlaczego to tak zagmatwanie wygląda? W OOP polimorfizm umożliwia wielopostaciowość obiektów. Jedna funkcja może obsłużyć wiele klas, przyjmując za argument jedynie wskaźnik lub referencję na klasę bazową. Przypomina to nieco port USB w komputerze. Podepniesz do niego i pendrive'a i klawiaturę. Oba urządzenia korzystają z jednego interfejsu, ale oferują różne funkcje.

1
komentarz 30 stycznia 2019 przez Seqak Początkujący (420 p.)
Kurczę, porównanie do USB całkiem nieźle pozwala sobie to zwizualizować. Wielkie dzięki za rady, chyba zaczynam łapać, gdzie źle podchodziłem do tematu.
+1 głos
odpowiedź 30 stycznia 2019 przez mokrowski VIP (146,960 p.)
Na początek, żebyś wiedział z czym masz do czynienia..

W większości języków wspierających polimorfizm (Java, Python, C#...), masz do czynienia właśnie z funkcjami wirtualnymi. Ukrywają to jednak mechanizmy danego języka (nie C++) i nie wiesz że za to płacisz. Mniejsza już o to ile i na jakim etapie bo zagadnienie ma dotyczyć C++.

W C++ masz do czynienia z 2 rodzajami polimorfizmu:
- statycznym - możliwy do stosowania na etapie kompilacji i którego działanie w programie nie implikuje żadnego dodatkowego narzutu na wywołanie funkcji. Jak i kiedy.. może innym razem.
- dynamicznym - stosowany na etapie uruchomienia programu, niezbędny w przypadkach gdy nie wiesz z jakiego rodzaju typem (a więc klasą) obiektu będziesz miał do czynienia a będziesz wiedział już w trakcie działania programu.

Ten drugi rodzaj polimorfizmu, wspierają metody wirtualne. A teraz o koszcie...

Od C++ wymaga się ekstremalnej wydajności. Stąd tak potężne mechanizmy wyboru jakie dostarcza ten język. Jeśli zastosujesz funkcję oznaczoną jako virtual, każde jej wywołanie połączone będzie z określeniem na rzecz którego obiektu (a dokładniej jakiego typu) będzie wywoływana. To określanie najczęściej odbywa się przez przeglądanie tablicy wywołań którą generuje kompilator dla klasy która posiada daną funkcję wirtualną (nazywaną vtable). Takie wywołanie (połączone z rozwiązaniem zagadki "na rzecz kogo"), kosztuje setki taktów zegara! Rozwiązanie zagadki działa zaś wyłącznie dla wskaźników i referencji na dany obiekt. To jest rozwiązanie kosztowne i wymagające świadomego użycia.

Załóżmy że jednak go użyłeś....

Oznacza to że będziesz chciał w klasach dziedziczących, przesłonić funkcję. Słowo kluczowe to "dziedziczących". W przypadku dziedziczenia, najczęściej dojdzie także do rozbudowania klasy dziecka o dodatkowe atrybuty. Pojawi się konieczność prawidłowego usunięcia obiektu z pamięci na etapie jego destrukcji. Stąd konieczne jest wtedy opatrywanie klas rodziców, wirtualnym destruktorem. Jeśli nie następuje rozbudowa o nowe atrybuty (rzadki przypadek), to destruktor wirtualny nie jest konieczny.

I na koniec tzw. "dobra reguła i praktyka". Jeśli pojawi się przy metodzie virtual, chcesz pewnie dziedziczyć, prawie na pewno będziesz miał destruktor wirtualny...

Na tym poprzestanę bo już długie wyszło... są oczywiście wyjątki, niuanse, reguły... ale pytałeś o zasadę...
komentarz 30 stycznia 2019 przez mokrowski VIP (146,960 p.)
x86, PowerPC, Avr, STM32 i radzę także wyciągnąć wnioski np. z tablicy wskaźników polimorficznych i tego co się tam dzieje oraz zaleceń wirtualizaca/brak dla norm embedded. Poza tym nie bierzecie pod uwagę trafiania w cache ;) Pomiary bracia pomiary....

Jak będę przy stacji cośtam przygotuję i sprawdzimy "jak jest". Chociaż poważnie zastanawiam się po co....
komentarz 30 stycznia 2019 przez mokrowski VIP (146,960 p.)

@adrian17,

Także też twierdzę że przesadziłeś jak na proste wprowadzenie do tematu.

Tak czepiasz się... i tak to będę traktował :-)

komentarz 30 stycznia 2019 przez adrian17 Ekspert (306,660 p.)

Chociaż poważnie zastanawiam się po co....

Też właśnie nie wiem. Dobrze że się zgadzamy, że to przesada na ten temat ;)

Tak czepiasz się... i tak to będę traktował :-)

(chyba dałeś zły cytat?)

komentarz 30 stycznia 2019 przez Seqak Początkujący (420 p.)

@mokrowski,
Sporo wiedzy, której trochę chyba brakuje w poradnikach (przynajmniej, w tych na które trafiałem). Dużo z rzeczy, o których napisałeś jest jeszcze dla mnie czarną magią, ale to dobrze. Takie wiadra zimnej wody się przydają i pokazują jak jeszcze dużo trzeba się nauczyć i jak dużo jest elementów programowania, które wpływają na jakość kodu. Dzięki za tak obszerną odpowiedź. :) 

komentarz 31 stycznia 2019 przez mokrowski VIP (146,960 p.)

@Seqak,

Proszę i nie ma sprawy. Pytaj a jeśli będę wiedział, odpowiem.

Podobne pytania

+1 głos
1 odpowiedź 309 wizyt
0 głosów
1 odpowiedź 84 wizyt
0 głosów
2 odpowiedzi 178 wizyt
pytanie zadane 1 czerwca 2019 w C i C++ przez Alan Kruszyński Obywatel (1,410 p.)

86,485 zapytań

135,241 odpowiedzi

300,484 komentarzy

57,233 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...