Czy WSZYSTKIE funkcje wirtualne muszą być implementowane w klasach pochodnych?


91

Może się to wydawać prostym pytaniem, ale nie mogę znaleźć odpowiedzi nigdzie indziej.

Załóżmy, że mam:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

Czy to w porządku, że klasa Derived nie implementuje funkcji bar ()? A co, jeśli nie WSZYSTKIE moje klasy pochodne wymagają funkcji bar (), ale niektóre tak. Czy wszystkie funkcje wirtualne abstrakcyjnej klasy bazowej muszą być zaimplementowane w klasach pochodnych, czy tylko te, które są czysto wirtualne? Dzięki

Odpowiedzi:


82

Klasy pochodne nie muszą same implementować wszystkich funkcji wirtualnych. Potrzebują tylko tych czystych . 1 Oznacza to, że Derivedklasa w pytaniu jest poprawna. To dziedziczy się barrealizacji ze swojej klasy przodka Abstract. (Zakłada się, że Abstract::barjest to gdzieś zaimplementowane. Kod w pytaniu deklaruje metodę, ale jej nie definiuje. Możesz zdefiniować ją w tekście, jak pokazuje odpowiedź Trenki , lub możesz zdefiniować ją osobno.)


1 A nawet wtedy, tylko jeśli klasa pochodna ma zostać utworzona . Jeśli klasa pochodna nie jest tworzona bezpośrednio, ale istnieje tylko jako klasa bazowa większej liczby klas pochodnych, to te klasy są odpowiedzialne za zaimplementowanie wszystkich swoich czystych metod wirtualnych. Klasa „średnia” w hierarchii może pozostawić niektóre czyste metody wirtualne nie zaimplementowane, podobnie jak klasa bazowa. Jeśli klasa „middle” robi wdrożenie czystej metody wirtualnej, a następnie jego potomkowie odziedziczą tę realizację, więc nie ma potrzeby ponownego wdrożenia to sami.


3
I nawet to (implementacja czystych funkcji wirtualnych) tylko wtedy, gdy mają być instancjonowane (w przeciwieństwie do bycia abstrakcyjną klasą bazową).
Christian Rau,

1
To jest to co myślałam. Ale robię to w swoim projekcie i otrzymuję błąd łączenia, który mówi, że istnieje „nierozwiązany zewnętrzny symbol” dla Derived :: bar (); Ale nigdy nie zadeklarowałem bar w Derived, więc dlaczego konsolidator szuka ciała funkcji?
mikestaub

1
@pixelpusher Oczywiście Derived::barma korpus funkcji, czyli Abstract::bar. Wydaje się więc, że jednostka tłumaczeniowa, w której została zdefiniowana (czy jest w ogóle zdefiniowana?), Nie jest połączona z jednostką tłumaczeniową, w której została wywołana.
Christian Rau

2
@Rob: They only need to implement the pure ones.To jest mylące. Klasy pochodne nie muszą też koniecznie implementować czystych funkcji wirtualnych.
Nawaz

Doceniam pomoc, ale @trenki trafił w sedno. Chociaż miałeś również rację, Christian Rau, ponieważ NIE zostało to zdefiniowane.
mikestaub

47

W klasach pochodnych trzeba zaimplementować tylko czyste metody wirtualne, ale nadal potrzebujesz definicji (a nie tylko deklaracji) innych metod wirtualnych. Jeśli go nie podasz, linker może bardzo narzekać.

Zatem samo wstawienie {}po opcjonalnej metodzie wirtualnej daje pustą domyślną implementację:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

Bardziej skomplikowana domyślna implementacja została jednak umieszczona w oddzielnym pliku źródłowym.


7

Standard ISO C ++ określa, że ​​należy zdefiniować wszystkie metody wirtualne klasy, które nie są czysto wirtualne.

Mówiąc najprościej, zasada jest taka:
Jeśli Twoja klasa pochodna zastępuje metodę wirtualną klasy bazowej, powinna również zawierać definicję. Jeśli nie, to klasa podstawowa powinna zawierać definicję tej metody.

Zgodnie z powyższą regułą w przykładowym kodzie virtual void bar();wymaga definicji w klasie bazowej.

Odniesienie:

C ++ 03 Standard: 10.3 Funkcje wirtualne [class.virtual]

Funkcja wirtualna zadeklarowana w klasie powinna zostać zdefiniowana lub zadeklarowana jako czysta (10.4) w tej klasie lub obie; ale nie jest wymagana żadna diagnostyka (3.2).

Więc albo powinieneś uczynić tę funkcję czystą wirtualną, albo podać jej definicję.

W gcc FAQ doccuments nim również:

Standard ISO C ++ określa, że ​​wszystkie metody wirtualne klasy, które nie są czysto wirtualne, muszą być zdefiniowane, ale nie wymagają żadnej diagnostyki w przypadku naruszenia tej reguły [class.virtual]/8. Opierając się na tym założeniu, GCC będzie emitować tylko niejawnie zdefiniowane konstruktory, operator przypisania, destruktor i wirtualną tabelę klasy w jednostce translacji, która definiuje swoją pierwszą taką metodę nieliniową.

Dlatego jeśli nie zdefiniujesz tej konkretnej metody, linker może narzekać na brak definicji dla pozornie niepowiązanych symboli. Niestety, aby poprawić ten komunikat o błędzie, może być konieczna zmiana konsolidatora, a nie zawsze można to zrobić.

Rozwiązaniem jest zapewnienie, że wszystkie metody wirtualne, które nie są czyste, są zdefiniowane. Należy zauważyć, że destruktor musi być zdefiniowany, nawet jeśli jest zadeklarowany jako czysto wirtualny [class.dtor]/7.


3

Tak, w porządku ... wystarczy zaimplementować czyste funkcje wirtualne, aby utworzyć instancję klasy pochodzącej z abstrakcyjnej klasy bazowej.


1

Tak, to prawda, że ​​klasa pochodna musi nadpisywać funkcję, która jest czystą wirtualną w klasie nadrzędnej. Klasa nadrzędna mająca czystą funkcję wirtualną jest nazywana klasą abstrakcyjną tylko dlatego, że jej klasa potomna musi dawać własne ciało czystej funkcji wirtualnej.

Dla normalnych funkcji wirtualnych: - Nie jest konieczne dalsze nadpisywanie ich, ponieważ niektóre klasy potomne mogą mieć tę funkcję, a inne mogą jej nie mieć.

Głównym celem mechanizmu funkcji wirtualnej jest polimorfizm w czasie wykonywania, niezależnie od tego, czy głównym celem czystej funkcji wirtualnej (klasy abstrakcyjnej) jest uczynienie obowiązkowym posiadanie tej samej nazwy funkcja w treści własnej.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.