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

Funkcje wirtualne

VPS Starter Arubacloud
+1 głos
1,053 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 (194,920 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 (344,100 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,340 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 Mędrzec (155,460 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 j23 Mędrzec (194,920 p.)

Takie wywołanie (połączone z rozwiązaniem zagadki "na rzecz kogo"), kosztuje setki taktów zegara!

Nie przesadziłeś z tymi setkami?

komentarz 30 stycznia 2019 przez mokrowski Mędrzec (155,460 p.)
Nie przesadziłem bo bez optymalizacji i bez uruchomionej dewirtualizacji w kompilatorze.
komentarz 30 stycznia 2019 przez j23 Mędrzec (194,920 p.)

Wyłuskanie wskaźnika do vtable ze wskaźnika do obiektu plus wyłuskanie wskaźnika do metody to nie jest jakaś strasznie skomplikowana operacja (strzelam, że ze dwie/trzy instrukcje mov)

komentarz 30 stycznia 2019 przez mokrowski Mędrzec (155,460 p.)
godbolt.org wyjaśnia sprawę.Także dla różnorodnych architektur i kombinacji optymalizacji.
1
komentarz 30 stycznia 2019 przez adrian17 Ekspert (344,100 p.)
edycja 30 stycznia 2019 przez adrian17

Mimo że to ważny temat przy optymalizacjach... jest wiele ważniejszych. W dodatku jeśli już zoptymalizowałeś program na tyle, że obciążenie metod wirtualnych jest zauważalne, to prawdopodobnie i tak już wiesz o ich koszcie.

Nie przesadziłem bo bez optymalizacji i bez uruchomionej dewirtualizacji w kompilatorze.

Bez optymalizacji też koszt w praktyce jest kilku mov'ów... W dodatku wyłączając optymalizacje pojawia się znacznie więcej wyraźniejszych spowolnień niż funkcje wirtualne; masz wtedy znacznie większe problemy.

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

A propo, dodatkowe małe czepialstwo:

Ukrywają to jednak mechanizmy danego języka (nie C++) i nie wiesz że za to płacisz.

W C#, tak samo jak w C++ie, musisz explicit poprosić by metoda była wirtualna i wiesz że za to płacisz.

komentarz 30 stycznia 2019 przez j23 Mędrzec (194,920 p.)

Czyli (prawie) dobrze strzelałem :) Dla x86 to jedna instrukcja mov:

mov     rax, qword ptr [rdi] ; <--- pobranie adresu vtable (w rdi adres klasy)
call    qword ptr [rax] <--- adres pierwszej metody w vtable

 

komentarz 30 stycznia 2019 przez mokrowski Mędrzec (155,460 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 Mędrzec (155,460 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 (344,100 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 Mędrzec (155,460 p.)

@Seqak,

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

Podobne pytania

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

92,453 zapytań

141,262 odpowiedzi

319,088 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

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

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...