Uczę się C ++ i dopiero zaczynam korzystać z funkcji wirtualnych.
Z tego, co przeczytałem (w książce i online), funkcje wirtualne to funkcje w klasie bazowej, które można zastąpić w klasach pochodnych.
Ale wcześniej w książce, kiedy uczyłem się o dziedziczeniu podstawowym, byłem w stanie zastąpić funkcje podstawowe w klasach pochodnych bez użycia virtual
.
Więc czego tu brakuje? Wiem, że w funkcjach wirtualnych jest coś więcej i wydaje się to ważne, więc chcę jasno określić, co to dokładnie jest. Po prostu nie mogę znaleźć prostej odpowiedzi online.
c++
virtual-functions
Jake Wilson
źródło
źródło
Odpowiedzi:
Oto, w jaki sposób zrozumiałem nie tylko jakie
virtual
funkcje, ale dlaczego są one wymagane:Powiedzmy, że masz te dwie klasy:
W swojej głównej funkcji:
Jak dotąd tak dobrze, prawda? Zwierzęta jedzą ogólne jedzenie, koty jedzą szczury, wszystko bez
virtual
.Zmieńmy to teraz trochę, aby
eat()
wywołano ją przez funkcję pośrednią (funkcja trywialna tylko dla tego przykładu):Teraz naszą główną funkcją jest:
Och, och ... wpuściliśmy kota
func()
, ale nie zje szczurów. Czy należy przeciążać,func()
aby to trwałoCat*
? Jeśli musisz wyprowadzić więcej zwierząt ze Zwierząt, wszystkie one potrzebują swoichfunc()
.Rozwiązaniem jest uczynienie
eat()
zAnimal
klasy funkcji wirtualnej:Główny:
Gotowy.
źródło
virtual
wprowadza dynamiczne wiązanie kontra statyczne i tak, to dziwne, jeśli pochodzisz z języków takich jak Java.Bez „wirtualnego” otrzymasz „wczesne wiązanie”. To, która implementacja zastosowanej metody zostanie podjęta w czasie kompilacji, zależy od typu wywoływanego wskaźnika.
Dzięki „wirtualnemu” uzyskuje się „późne wiązanie”. O tym, która implementacja metody jest używana, decyduje się w czasie wykonywania na podstawie typu wskazywanego obiektu - jak to było pierwotnie zbudowane. Niekoniecznie tak myślisz w oparciu o typ wskaźnika, który wskazuje na ten obiekt.
EDYCJA - zobacz to pytanie .
Ponadto - ten samouczek obejmuje wczesne i późne wiązanie w C ++.
źródło
main
funkcji itp. Wskaźnik-do-pochodna domyślnie rzutuje na wskaźnik do bazy (więcej wyspecjalizowanych pośrednio przechodzi na bardziej ogólne). Odwrotnie, potrzebujesz jawnej obsady, zwykledynamic_cast
. Wszystko inne - bardzo podatne na niezdefiniowane zachowanie, więc upewnij się, że wiesz, co robisz. Według mojej najlepszej wiedzy nie zmieniło się to nawet przed C ++ 98.Aby to zademonstrować, potrzebujesz co najmniej 1 poziomu dziedziczenia i spuścizny. Oto bardzo prosty przykład:
źródło
Potrzebujesz wirtualnych metod do bezpiecznego downcastingu , prostoty i zwięzłości .
To właśnie robią metody wirtualne: bezpiecznie spuszczają, z pozornie prostym i zwięzłym kodem, unikając niebezpiecznych ręcznych rzutów w bardziej złożonym i szczegółowym kodzie, który w przeciwnym razie miałbyś.
Metoda nie-wirtualna ⇒ wiązanie statyczne
Poniższy kod jest celowo „niepoprawny”. Nie deklaruje
value
metody jakovirtual
i dlatego generuje niezamierzony „zły” wynik, a mianowicie 0:W wierszu skomentowanym jako „zły”
Expression::value
wywoływana jest metoda, ponieważ typem znanym statystycznie (typem znanym w czasie kompilacji) jestExpression
, avalue
metoda nie jest wirtualna.Metoda wirtualna ⇒ wiązanie dynamiczne.
Deklaracja
value
jakvirtual
w typie znanym statycznieExpression
zapewnia, że każde wywołanie sprawdzi, jaki jest rzeczywisty typ obiektu i wywoła odpowiednią implementacjęvalue
dla tego typu dynamicznego :Tutaj dane wyjściowe są
6.86
takie, jakie powinny być, ponieważ wirtualna metoda jest nazywana wirtualnie . Jest to również nazywane dynamicznym wiązaniem połączeń. Przeprowadzane jest małe sprawdzenie, znalezienie rzeczywistego typu dynamicznego obiektu i wywołanie odpowiedniej implementacji metody dla tego typu dynamicznego.Odpowiednia implementacja należy do najbardziej konkretnej (najbardziej wyprowadzonej) klasy.
Zauważ, że implementacje metod w klasach pochodnych nie są tutaj zaznaczone
virtual
, ale są oznaczoneoverride
. Mogą być oznaczone,virtual
ale są automatycznie wirtualne. Teoverride
, zapewnia, że jeśli słowo kluczowe jest nie taka metoda wirtualna w jakiejś klasie bazowej, a następnie dostaniesz błąd (co jest pożądane).Brzydota robienia tego bez wirtualnych metod
Bez tego
virtual
nie byłoby konieczne wdrożenie jakiejś wersji dynamicznego powiązania zrób to sam . To na ogół wiąże się z niebezpiecznym ręcznym downcastingiem, złożonością i gadatliwością.W przypadku pojedynczej funkcji, jak tutaj, wystarczy przechowywać wskaźnik funkcji w obiekcie i wywoływać go za pomocą tego wskaźnika funkcji, ale mimo to wiąże się to z pewnymi niebezpiecznymi spadkami, złożonością i szczegółowością, a mianowicie:
Jednym pozytywnym sposobem spojrzenia na to jest, jeśli napotkasz niebezpieczne downcasting, złożoność i gadatliwość, jak wyżej, to często wirtualna metoda lub metody mogą naprawdę pomóc.
źródło
Funkcje wirtualne służą do wspierania polimorfizmu w środowisku wykonawczym .
To znaczy, wirtualne słowo kluczowe mówi kompilatorowi, aby nie podejmował decyzji (o wiązaniu funkcji) w czasie kompilacji, a raczej odkładał ją na czas wykonywania " .
Możesz uczynić funkcję wirtualną, poprzedzając słowo kluczowe
virtual
w deklaracji klasy podstawowej. Na przykład,Gdy klasa podstawowa ma wirtualną funkcję składową, każda klasa dziedzicząca od klasy bazowej może ponownie zdefiniować funkcję z dokładnie tym samym prototypem, tj. Można zdefiniować tylko funkcjonalność, a nie interfejs funkcji.
Wskaźnik klasy podstawowej może służyć do wskazywania obiektu klasy podstawowej, a także obiektu klasy pochodnej.
źródło
Jeśli klasą podstawową jest
Base
, a klasą pochodną jestDer
, możesz miećBase *p
wskaźnik, który faktycznie wskazuje na instancjęDer
. Gdy zadzwoniszp->foo();
, jeśli niefoo
jest wirtualny, wówczas wykonywana jest jego wersja, ignorując fakt, że faktycznie wskazuje na . Jeśli foo jest wirtualny, wykonuje przesłonięcie „najbardziej liściaste” , w pełni uwzględniając rzeczywistą klasę wskazanego elementu. Tak więc różnica między wirtualnym a nie-wirtualnym jest w rzeczywistości bardzo istotna: ta pierwsza pozwala na polimorfizm w czasie wykonywania , podstawową koncepcję programowania OO, podczas gdy druga nie.Base
p
Der
p->foo()
foo
źródło
Wyjaśnienie Need for Virtual Function [Łatwy do zrozumienia]
Dane wyjściowe będą:
Ale z funkcją wirtualną:
Dane wyjściowe będą:
Dzięki funkcji wirtualnej można uzyskać polimorfizm środowiska uruchomieniowego.
źródło
Chciałbym dodać jeszcze jedno użycie funkcji wirtualnej, chociaż korzysta ona z tej samej koncepcji, co podane powyżej odpowiedzi, ale chyba warto o tym wspomnieć.
WIRTUALNY DESTRUCTOR
Rozważ ten program poniżej, bez deklarowania destruktora klasy bazowej jako wirtualnego; pamięć Cat nie może zostać wyczyszczona.
Wynik:
Wynik:
źródło
without declaring Base class destructor as virtual; memory for Cat may not be cleaned up.
Gorzej niż to. Usunięcie obiektu pochodnego za pomocą podstawowego wskaźnika / odwołania jest czystym niezdefiniowanym zachowaniem. Więc nie chodzi tylko o to, że część pamięci może przeciekać. Raczej program jest źle sformułowany, więc kompilator może go przekształcić w cokolwiek: kod maszynowy, który akurat działa dobrze, nie robi nic, przywołuje demony z nosa itp. Dlatego właśnie, jeśli program jest zaprojektowany w taki sposób sposobem, w jaki użytkownik może usunąć pochodną instancję za pomocą referencji podstawowej, baza musi mieć wirtualny destruktorMusisz rozróżnić nadpisywanie i przeciążanie. Bez
virtual
słowa kluczowego przeciążasz tylko metodę klasy bazowej. Oznacza to tylko ukrywanie się. Załóżmy, że masz klasę podstawowąBase
i pochodną,Specialized
które oba implementująvoid foo()
. Teraz masz wskaźnik doBase
wskazania wystąpieniaSpecialized
. Kiedy ją wywołujeszfoo()
, możesz zaobserwować różnicę, któravirtual
czyni: Jeśli metoda jest wirtualna,Specialized
zostanie zastosowana implementacja , jeśli jej nie ma,Base
wybierana będzie wersja z . Najlepiej jest nigdy nie przeciążać metod z klasy podstawowej. Uczynienie metody niewirtualną to sposób, w jaki autor mówi, że jej rozszerzenie w podklasach nie jest zamierzone.źródło
virtual
ciebie nie jesteś przeciążony. Jesteś shadowing . Jeśli klasa podstawowaB
ma jedną lub więcej funkcjifoo
, a klasa pochodnaD
definiujefoo
nazwę, którafoo
ukrywa wszystkie tefoo
-sB
. Osiągane są jakoB::foo
przy użyciu rozdzielczości zakresu. Aby promowaćB::foo
funkcje wD
celu przeciążenia, musisz użyćusing B::foo
.Szybka odpowiedź:
W Bjarne Stroustrup Programowanie w C ++: zasady i praktyka, (14.3):
1. Zastosowanie dziedziczenia, polimorfizmu w czasie wykonywania i enkapsulacji jest najczęstszą definicją programowania obiektowego .
2. Nie można kodować funkcji, aby była szybsza lub zużywała mniej pamięci, używając innych funkcji języka do wybierania alternatyw w czasie wykonywania. Bjarne Stroustrup C ++ Programowanie: zasady i praktyka. (14.3.1) .
3. Coś, co powiedzieć, która funkcja jest rzeczywiście wywoływana, gdy wywołujemy klasę podstawową zawierającą funkcję wirtualną.
źródło
Mam odpowiedź w formie rozmowy, aby lepiej przeczytać:
Dlaczego potrzebujemy funkcji wirtualnych?
Z powodu polimorfizmu.
Co to jest polimorfizm?
Fakt, że wskaźnik bazowy może również wskazywać na obiekty typu pochodnego.
Jak ta definicja polimorfizmu prowadzi do potrzeby funkcji wirtualnych?
Przez wczesne wiązanie .
Co jest wcześnie wiążące?
Wczesne wiązanie (wiązanie w czasie kompilacji) w C ++ oznacza, że wywołanie funkcji jest ustalone przed uruchomieniem programu.
Więc...?
Jeśli więc użyjesz typu podstawowego jako parametru funkcji, kompilator rozpozna tylko interfejs podstawowy, a jeśli wywołasz tę funkcję z dowolnymi argumentami z klas pochodnych, zostanie ona odcięta, co nie jest tym, co chcesz zrobić.
Jeśli nie to chcemy, dlaczego jest to dozwolone?
Ponieważ potrzebujemy polimorfizmu!
Jaka jest zatem korzyść z polimorfizmu?
Możesz użyć wskaźnika typu podstawowego jako parametru pojedynczej funkcji, a następnie w czasie wykonywania programu możesz uzyskać dostęp do każdego z pochodnych interfejsów typu (np. Ich funkcji składowych) bez żadnych problemów, korzystając z dereferencji tego singla wskaźnik bazowy.
Nadal nie wiem, jakie funkcje wirtualne są dobre dla ...! To było moje pierwsze pytanie!
to dlatego, że zadałeś zbyt wcześnie pytanie!
Dlaczego potrzebujemy funkcji wirtualnych?
Załóżmy, że wywołałeś funkcję ze wskaźnikiem podstawowym, który miał adres obiektu z jednej z jego klas pochodnych. Jak już mówiliśmy o tym powyżej, w czasie wykonywania wskaźnik ten zostaje zdereferencjonowany, jak na razie jednak spodziewamy się, że zostanie wykonana metoda (== funkcja członka) „z naszej klasy pochodnej”! Jednak ta sama metoda (ta, która ma ten sam nagłówek) jest już zdefiniowana w klasie bazowej, więc dlaczego twój program miałby zadawać sobie trud wyboru innej metody? Innymi słowy, jak możesz odróżnić ten scenariusz od tego, co normalnie zdarzało się wcześniej?
Krótka odpowiedź to „funkcja wirtualnego elementu członkowskiego w bazie”, a nieco dłuższa odpowiedź brzmi: „na tym etapie, jeśli program zobaczy funkcję wirtualną w klasie podstawowej, wie (zdaje sobie sprawę), że próbujesz użyć polimorfizm ”i tak idzie do klas pochodnych (przy użyciu tabeli v , formy późnego wiązania) w celu znalezienia innej metody z tym samym nagłówkiem, ale z - prawdopodobnie - inną implementacją.
Dlaczego inna implementacja?
Ty golicy! Idź przeczytać dobrą książkę !
OK, czekaj czekaj czekaj, dlaczego ktoś miałby zadawać sobie trud korzystania ze wskaźników bazowych, skoro mógł po prostu używać wskaźników pochodnych? Jesteś sędzią, czy ten cały ból głowy jest tego wart? Spójrz na te dwa fragmenty:
// 1:
// 2:
OK, chociaż myślę, że 1 jest wciąż lepszy niż 2 , możesz napisać 1 tak:
// 1:
a ponadto powinieneś zdawać sobie sprawę, że jest to tylko przemyślane wykorzystanie wszystkich rzeczy, które ci wyjaśniłem do tej pory. Zamiast tego załóżmy na przykład sytuację, w której miałeś w swoim programie funkcję, która korzystała z metod odpowiednio z każdej z klas pochodnych (getMonthBenefit ()):
Teraz spróbuj ponownie napisać to bez żadnych problemów!
I faktycznie może to być jeszcze wymyślony przykład!
źródło
Kiedy masz funkcji w klasie bazowej, można
Redefine
alboOverride
w jego klasie pochodnej.Przedefiniowanie metody : Nowa implementacja metody klasy bazowej jest podana w klasie pochodnej. Nie ułatwiają
Dynamic binding
.Przesłanianie metody :
Redefining
avirtual method
klasy bazowej w klasie pochodnej. Metoda wirtualna ułatwia dynamiczne wiązanie .Więc kiedy powiedziałeś:
nie przesłaniałeś tego, ponieważ metoda w klasie bazowej nie była wirtualna, raczej ją przedefiniowałeś
źródło
Pomaga, jeśli znasz podstawowe mechanizmy. C ++ formalizuje niektóre techniki kodowania stosowane przez programistów C, „klasy” zastępowane za pomocą „nakładek” - struktury ze wspólnymi sekcjami nagłówka byłyby używane do obsługi obiektów różnego typu, ale z pewnymi typowymi danymi lub operacjami. Zwykle podstawowa struktura nakładki (część wspólna) ma wskaźnik do tabeli funkcji, która wskazuje na inny zestaw procedur dla każdego typu obiektu. C ++ robi to samo, ale ukrywa mechanizmy, tj. C ++,
ptr->func(...)
gdzie func jest wirtualny jak C(*ptr->func_table[func_num])(ptr,...)
, gdzie zmiany między klasami pochodnymi to zawartość tabeli func_table. [Nie-wirtualna metoda ptr-> func () po prostu tłumaczy się na mangled_func (ptr, ..).]Wynikiem tego jest to, że musisz tylko zrozumieć klasę podstawową, aby wywołać metody klasy pochodnej, tj. Jeśli procedura rozumie klasę A, możesz przekazać jej wskaźnik pochodnej klasy B, wówczas wywoływane metody wirtualne będą tymi z B zamiast A, ponieważ przeglądasz tabelę funkcji, na którą wskazuje B.
źródło
Słowo kluczowe virtual mówi kompilatorowi, że nie powinien wykonywać wczesnego wiązania. Zamiast tego powinien automatycznie zainstalować wszystkie mechanizmy niezbędne do wykonania późnego wiązania. Aby to osiągnąć, typowy kompilator 1 tworzy pojedynczą tabelę (zwaną VTABLE) dla każdej klasy zawierającej funkcje wirtualne. Kompilator umieszcza adresy funkcji wirtualnych dla tej konkretnej klasy w VTABLE. W każdej klasie z funkcjami wirtualnymi potajemnie umieszcza wskaźnik o nazwie vpointer (w skrócie VPTR), który wskazuje na VTABLE dla tego obiektu. Kiedy wykonujesz wirtualne wywołanie funkcji za pomocą wskaźnika klasy bazowej, kompilator po cichu wstawia kod, aby pobrać VPTR i wyszukać adres funkcji w VTABLE, wywołując w ten sposób poprawną funkcję i powodując opóźnione wiązanie.
Więcej szczegółów w tym linku http://cplusplusinterviews.blogspot.sg/2015/04/virtual-mechanism.html
źródło
Te wirtualne siły słów kluczowych kompilator wybrać wdrożenie metody zdefiniowane w obiektu klasy zamiast w pointer w klasie.
W powyższym przykładzie Shape :: getName będzie wywoływany domyślnie, chyba że getName () jest zdefiniowany jako wirtualny w klasie bazowej Shape. Zmusza to kompilator do szukania implementacji getName () w klasie Triangle, a nie w klasie Shape.
Tabela wirtualny jest mechanizm, w którym kompilator śledzi różne implementacje wirtualnych sposobu podklasy. Nazywa się to również dynamiczny sklepie, a tam jest jakiś narzut związany z nim.
Wreszcie, dlaczego wirtualny jest nawet potrzebny w C ++, dlaczego nie uczynić go domyślnym zachowaniem jak w Javie?
źródło
Dlaczego potrzebujemy funkcji wirtualnych?
Funkcje wirtualne unikają niepotrzebnego problemu z rzutowaniem typu, a niektórzy z nas mogą zastanawiać się, dlaczego potrzebujemy funkcji wirtualnych, gdy możemy użyć wskaźnika klasy pochodnej, aby wywołać funkcję specyficzną w klasie pochodnej! Odpowiedź brzmi - niweczy całą ideę dziedziczenia w dużym systemie rozwój, w którym bardzo pożądany jest obiekt klasy bazowej pojedynczego wskaźnika.
Porównajmy dwa proste programy, aby zrozumieć znaczenie funkcji wirtualnych:
Program bez funkcji wirtualnych:
WYNIK:
Program z funkcją wirtualną:
WYNIK:
Dokładnie analizując oba wyjścia, można zrozumieć znaczenie funkcji wirtualnych.
źródło
Odpowiedź OOP: polimorfizm podtypu
W C ++ wirtualne metody są potrzebne do realizacji polimorfizmu , a dokładniej polimorfizmu podtypów lub podtypów, jeśli zastosujesz definicję z wikipedii.
Wikipedia, Subtyping, 2019-01-09: W teorii języka programowania podtyp (również polimorfizm podtypu lub polimorfizm inkluzyjny) jest formą polimorfizmu typu, w którym podtyp jest typem danych powiązanym z innym typem danych (nadtypem) według pewnego pojęcia podstawialności, co oznacza, że elementy programu, zwykle podprogramy lub funkcje, napisane w celu działania na elementach nadtypu, mogą również działać na elementach podtypu.
UWAGA: Podtyp oznacza klasę podstawową, a podtyp oznacza klasę odziedziczoną.
Dalsza lektura dotycząca polimorfizmu podtypu
Odpowiedź techniczna: dynamiczna wysyłka
Jeśli masz wskaźnik do klasy bazowej, wówczas wywołanie metody (która jest zadeklarowana jako wirtualna) zostanie wysłana do metody rzeczywistej klasy tworzonego obiektu. W ten sposób realizowany jest polimorfizm podtypu to C ++.
Dalsze czytanie Polimorfizm w C ++ i Dynamic Dispatch
Realizacja Odpowiedź: Tworzy wpis vtable
Dla każdego modyfikatora „wirtualnego” w metodach kompilatory C ++ zwykle tworzą wpis w tabeli vt klasy, w której metoda jest zadeklarowana. W ten sposób powszechny kompilator C ++ realizuje dynamiczną wysyłkę .
Dalsza lektura tabel
Przykładowy kod
Wyjście kodu przykładowego
Schemat klasy UML przykładowego kodu
źródło
Oto kompletny przykład ilustrujący, dlaczego używana jest metoda wirtualna.
źródło
Jeśli chodzi o wydajność, funkcje wirtualne są nieco mniej wydajne niż funkcje wczesnego wiązania.
„Ten mechanizm wirtualnych wywołań można uczynić prawie tak samo skutecznym jak mechanizm„ normalnych wywołań funkcji ”(w granicach 25%). Narzut miejsca to jeden wskaźnik w każdym obiekcie klasy z funkcjami wirtualnymi plus jeden vtbl dla każdej takiej klasy” [ A tournee po C ++ Bjarne Stroustrup]
źródło
if(param1>param2) return cst;
gdzie kompilator może w niektórych przypadkach zredukować całe wywołanie funkcji do stałej).W projektowaniu interfejsu stosowane są metody wirtualne. Na przykład w systemie Windows istnieje interfejs o nazwie IUnknown, taki jak poniżej:
Metody te należy pozostawić użytkownikowi interfejsu do wdrożenia. Są niezbędne do tworzenia i niszczenia niektórych obiektów, które muszą odziedziczyć IUnknown. W takim przypadku środowisko wykonawcze zna trzy metody i oczekuje, że zostaną wdrożone, gdy je wywoła. W pewnym sensie działają one jak umowa między samym przedmiotem a tym, co go używa.
źródło
the run-time is aware of the three methods and expects them to be implemented
Ponieważ są one czysto wirtualne, nie ma możliwości utworzenia instancjiIUnknown
, dlatego wszystkie podklasy muszą implementować wszystkie takie metody w celu jedynie kompilacji. Nie ma niebezpieczeństwa, że ich nie zaimplementujesz, a dowiesz się tego tylko w czasie wykonywania (ale oczywiście można je błędnie zaimplementować, oczywiście!). I wow, dzisiaj nauczyłem się#define
makra Windows ze słoweminterface
, prawdopodobnie dlatego, że ich użytkownicy nie mogą (A) zobaczyć prefiksuI
w nazwie lub (B) spojrzeć na klasę, by zobaczyć, że to interfejs. Ughmyślę, że odwołujesz się do faktu, że gdy metoda zostanie uznana za wirtualną, nie musisz używać słowa kluczowego „virtual” w przypadku przesłonięć.
Jeśli nie użyjesz „wirtualnego” w deklaracji foo Base, wówczas foo Derived po prostu ją zacieni.
źródło
Oto połączona wersja kodu C ++ dla pierwszych dwóch odpowiedzi.
Dwa różne wyniki to:
Bez #define virtual wiąże się w czasie kompilacji. Reklama Animal * i func (Animal *) wskazują na metodę mówi () Animal.
Z #define virtual wiąże się w czasie wykonywania. Reklama Dog * d, Animal * i func (Animal *) wskazują / odnoszą się do metody Dog's mówi (), ponieważ Pies jest ich typem obiektu. O ile nie zdefiniowano metody „woof” [Dog's mówi () ”, będzie ona przeszukiwana jako pierwsza w drzewie klas, tzn. Klasy pochodne mogą nadpisywać metody klas podstawowych [Animal's said ()].
Warto zauważyć, że wszystkie atrybuty klas (dane i metody) w Pythonie są w rzeczywistości wirtualne . Ponieważ wszystkie obiekty są tworzone dynamicznie w czasie wykonywania, nie ma deklaracji typu ani potrzeby używania słowa kluczowego virtual. Poniżej znajduje się wersja kodu Pythona:
Dane wyjściowe to:
która jest identyczna z wirtualną definicją C ++. Zauważ, że d i reklama to dwie różne zmienne wskaźnikowe odnoszące się do tej samej instancji Dog. Wyrażenie (reklama jest d) zwraca wartość True, a ich wartości są takie same < główny obiekt .Dog w 0xb79f72cc>.
źródło
Potrzebujemy wirtualnych metod wspierania „polimorfizmu w czasie wykonywania”. Gdy odwołujesz się do obiektu klasy pochodnej za pomocą wskaźnika lub odwołania do klasy bazowej, możesz wywołać funkcję wirtualną dla tego obiektu i wykonać wersję funkcji klasy pochodnej.
źródło
Czy znasz wskaźniki funkcji? Funkcje wirtualne są podobnym pomysłem, z tym wyjątkiem, że można łatwo powiązać dane z funkcjami wirtualnymi (jako członkami klasy). Powiązanie danych ze wskaźnikami funkcji nie jest tak łatwe. Dla mnie jest to główne rozróżnienie pojęciowe. Wiele innych odpowiedzi tutaj mówi tylko „ponieważ… polimorfizm!”
źródło
Najważniejsze jest to, że funkcje wirtualne ułatwiają życie. Skorzystajmy z niektórych pomysłów M. Perry i opiszmy, co by się stało, gdybyśmy nie mieli funkcji wirtualnych, a zamiast tego moglibyśmy używać tylko wskaźników funkcji członkowskich. W normalnym oszacowaniu bez funkcji wirtualnych mamy:
Ok, więc to wiemy. Teraz spróbujmy to zrobić za pomocą wskaźników funkcji członkowskich:
Chociaż możemy robić pewne rzeczy za pomocą wskaźników funkcji członkowskich, nie są one tak elastyczne jak funkcje wirtualne. Używanie wskaźnika funkcji członka w klasie jest trudne; wskaźnik funkcji składowej prawie, przynajmniej w mojej praktyce, zawsze musi być wywoływany w funkcji głównej lub z funkcji składowej, jak w powyższym przykładzie.
Z drugiej strony, funkcje wirtualne, chociaż mogą mieć narzut wskaźnika funkcji, znacznie upraszczają sprawy.
EDYCJA: Istnieje inna metoda podobna do funkcji wirtualnej eddietree: c ++ vs wskaźnik funkcji składowej (porównanie wydajności) .
źródło