Z podaną poniżej definicją struktury ...
struct A {
virtual void hello() = 0;
};
Podejście nr 1:
struct B : public A {
virtual void hello() { ... }
};
Podejście nr 2:
struct B : public A {
void hello() { ... }
};
Czy jest jakaś różnica między tymi dwoma sposobami zastąpienia funkcji hello?
c++
overriding
virtual-functions
Anarki
źródło
źródło
Odpowiedzi:
Są dokładnie takie same. Nie ma między nimi żadnej różnicy poza tym, że pierwsze podejście wymaga więcej pisania i jest potencjalnie jaśniejsze.
źródło
override
słowa kluczowego.„Wirtualność” funkcji jest propagowana niejawnie, jednak przynajmniej jeden kompilator, którego używam, wygeneruje ostrzeżenie, jeśli
virtual
słowo kluczowe nie zostanie użyte jawnie, więc możesz go użyć, aby zachować kompilator w spokoju.Z czysto stylistycznego punktu widzenia, w tym
virtual
słowa kluczowego wyraźnie „reklamuje” fakt, że funkcja jest wirtualna. Będzie to ważne dla każdej dalszej podklasy B bez konieczności sprawdzania definicji A. Dla głębokich hierarchii klasowych staje się to szczególnie ważne.źródło
Słowo
virtual
kluczowe nie jest konieczne w klasie pochodnej. Oto dokumentacja pomocnicza z C ++ Draft Standard (N3337) (wyróżnienie moje):źródło
Nie,
virtual
słowo kluczowe w zastępowaniu funkcji wirtualnej klas pochodnych nie jest wymagane. Warto jednak wspomnieć o związanej z tym pułapce: niepowodzeniu zastąpienia funkcji wirtualnej.Brak ręcznego występuje, jeśli zamierzają zastąpić funkcję wirtualną w klasie pochodnej, ale popełniła błędu w podpisie tak, że deklaruje nową i inną funkcję wirtualną. Ta funkcja może być przeciążeniem funkcji klasy podstawowej lub może różnić się nazwą. Bez względu na to, czy użyjesz
virtual
słowa kluczowego w deklaracji funkcji klasy pochodnej, kompilator nie będzie w stanie stwierdzić, czy zamierzasz zastąpić funkcję z klasy podstawowej.Na tę pułapkę na szczęście jednak reaguje funkcja jawnego przesłonięcia języka C ++ 11 , która pozwala kodowi źródłowemu wyraźnie określić, że funkcja członka ma zastąpić funkcję klasy podstawowej:
Kompilator wygeneruje błąd czasu kompilacji, a błąd programowania stanie się natychmiast oczywisty (być może funkcja Pochodna powinna przyjąć
float
argument jako argument).Zobacz WP: C ++ 11 .
źródło
Dodanie słowa kluczowego „wirtualnego” jest dobrą praktyką, ponieważ poprawia czytelność, ale nie jest konieczne. Funkcje zadeklarowane jako wirtualne w klasie bazowej i mające tę samą sygnaturę w klasach pochodnych są domyślnie uważane za „wirtualne”.
źródło
Kompilator nie ma różnicy, gdy piszesz
virtual
w klasie pochodnej lub pomijasz ją.Ale musisz spojrzeć na klasę podstawową, aby uzyskać te informacje. Dlatego polecam dodać
virtual
słowo kluczowe również w klasie pochodnej, jeśli chcesz pokazać człowiekowi, że ta funkcja jest wirtualna.źródło
Istnieje znaczna różnica, gdy masz szablony i zaczynasz brać klasy podstawowe jako parametry szablonu:
Część zabawy jest to, że można teraz definiować funkcje interfejsu i non-Interface później do definiowania klas. Jest to przydatne do współpracy interfejsów między bibliotekami (nie polegaj na tym jako standardowym procesie projektowania pojedynczej biblioteki). Nic nie kosztuje, aby pozwolić na to dla wszystkich twoich zajęć - możesz nawet
typedef
B do czegoś, jeśli chcesz.Zauważ, że jeśli to zrobisz, możesz chcieć zadeklarować również konstruktory kopiowania / przenoszenia jako szablony: zezwolenie na konstruowanie z różnych interfejsów pozwala „rzutować” między różnymi
B<>
typami.Wątpliwe jest, czy należy dodać obsługę
const A&
wt_hello()
. Zwykle powodem tego przepisywania jest odejście od specjalizacji opartej na dziedziczeniu na specjalizacji opartej na szablonie, głównie ze względu na wydajność. Jeśli nadal będziesz obsługiwać stary interfejs, nie będziesz w stanie wykryć (lub powstrzymać) starego użycia.źródło
virtual
Kluczowe powinny być dodane do funkcji klasy bazowej, aby je przeciążać. W twoim przykładziestruct A
jest to klasa podstawowa.virtual
nic nie znaczy do używania tych funkcji w klasie pochodnej. Jednak jeśli chcesz, aby twoja klasa pochodna była również samą klasą bazową i chcesz, aby ta funkcja była nadpisywalna, wtedy musiałbyś jąvirtual
tam umieścić .Tutaj
C
dziedziczy poB
, więcB
nie jest klasą bazową (jest to również klasa pochodna), aC
jest klasą pochodną. Schemat dziedziczenia wygląda następująco:Dlatego należy umieścić
virtual
funkcje przed potencjalnymi klasami podstawowymi, które mogą mieć dzieci.virtual
pozwala twoim dzieciom zastąpić twoje funkcje. Nie ma nic złego w umieszczaniuvirtual
przed funkcjami wewnątrz klas pochodnych, ale nie jest to wymagane. Jest to zalecane, ponieważ jeśli ktoś chciałby odziedziczyć po klasie pochodnej, nie byłby zadowolony, że zastąpienie metody nie działa zgodnie z oczekiwaniami.Dlatego stawiaj
virtual
przed funkcjami we wszystkich klasach związanych z dziedziczeniem, chyba że wiesz na pewno, że klasa nie będzie miała dzieci, które musiałyby zastąpić funkcje klasy podstawowej. To dobra praktyka.źródło
Z pewnością dołączę słowo kluczowe Virtual dla klasy potomnej, ponieważ
źródło