2010-02-04 10 views
65

Jeśli mam klasę podstawową z wirtualnym destruktorem. Czy klasa pochodna również deklaruje wirtualny destruktor?Czy wirtualne destruktory są dziedziczone?

class base { 
public: 
    virtual ~base() {} 
}; 

class derived : base { 
public: 
    virtual ~derived() {} // 1) 
    ~derived() {} // 2) 
}; 

pytania betonowe:

  1. wynosi 1) i 2) są takie same? Czy 2) jest automatycznie wirtualny ze względu na swoją podstawę, czy też "zatrzymuje" wirtualność?
  2. Czy pochodny destruktor może zostać pominięty, jeśli nie ma nic wspólnego?
  3. Jaka jest najlepsza praktyka do deklarowania wyprowadzonego destruktora? Zadeklarować, że jest to wirtualne, nie-wirtualne lub, jeśli to możliwe, pominąć?

Odpowiedz

82
  1. Tak, są takie same. Klasa pochodna, która nie deklaruje czegoś wirtualnego, nie powstrzymuje go od bycia wirtualnym. W rzeczywistości nie ma możliwości zatrzymania dowolnej metody (włączając destruktor) od wirtualności w klasie pochodnej, jeśli była ona wirtualna w klasie bazowej. W> = C++ 11 można użyć final, aby zapobiec nadpisywaniu w klasach pochodnych, ale to nie przeszkadza, aby było wirtualne.
  2. Tak, destruktor w klasie pochodnej można pominąć, jeśli nie ma nic wspólnego. I nie ma znaczenia, czy jest to wirtualne.
  3. Pominęłbym go, jeśli to możliwe. I zawsze używam ponownie słowa kluczowego virtual dla funkcji wirtualnych w klasach pochodnych dla jasności. Ludzie nie powinni przechodzić przez hierarchię dziedziczenia, aby zorientować się, że funkcja jest wirtualna. Dodatkowo, jeśli klasa jest kopiowalna lub przenośna bez konieczności zadeklarowania własnej kopii lub przeniesienia konstruktorów, deklarowanie dowolnego destruktora (nawet jeśli zdefiniujesz go jako default) zmusi Cię do zadeklarowania kopiowania i przeniesienia konstruktorów i operatorów przypisania, jeśli chcę ich, ponieważ kompilator nie będzie już dla ciebie ich używał.

Jako mały punkt dla pozycji 3. W uwagach zaznaczono, że jeśli destruktor jest nieokreślony, kompilator generuje domyślny (który jest nadal wirtualny). Ten domyślny jest funkcją inline.

Funkcje wbudowane potencjalnie narażają więcej programu na zmiany w innych częściach programu i sprawiają, że trudno jest uzyskać kompatybilność binarną dla bibliotek współdzielonych. Ponadto zwiększone sprzężenie może spowodować wiele ponownych kompilacji w obliczu pewnych rodzajów zmian. Na przykład, jeśli zdecydujesz, że naprawdę chcesz implementację swojego wirtualnego destruktora, każdy kod, który go wywołał, będzie musiał zostać zrekompilowany. Podczas gdy zadeklarowałeś to w treści klasy, a następnie zdefiniowałeś ją jako pustą w pliku .cpp, dobrze byłoby zmienić ją bez rekompilacji.

Moim osobistym wyborem byłoby pominięcie go, gdy jest to możliwe. Moim zdaniem powoduje to zaśmiecanie kodu, a kompilator może czasami robić nieco bardziej wydajne rzeczy z domyślną implementacją nad pustą. Ale istnieją ograniczenia, które mogą być pod tym, że to zły wybór.

+0

Twoje ostatnie zdanie prawdopodobnie powinno brzmieć "nie powinno" zamiast "powinien". –

+0

@ Chr. Lutz, wyprzedzam cię o to. Zostało to teraz poddane edycji. :-) – Omnifarious

+1

Nie zgadzam się z częścią "omit". Oświadczenie go w nagłówku nie kosztuje wiele i definiuje go (pusty obiekt) w źródle. Jeśli to zrobisz, zawsze możesz wrócić i dodać kilka kroków (rejestrowanie?) Bez zmuszania klientów do ponownej kompilacji. –

1

Wirtualna funkcja członka będzie implicite każdego przeciążenia tej funkcji wirtualnej.

Więc wirtualny w 1) jest "opcjonalny", a destruktor klasy podstawowej jest wirtualny sprawia, że ​​wszystkie destruktory potomne również są wirtualne.

1
  1. Destruktor jest automatycznie wirtualny, tak jak w przypadku wszystkich metod. Nie można zatrzymać metody od bycia wirtualnym w C++ (jeśli zostało już zadeklarowane jako wirtualne, tj.nie ma odpowiednika "końcowego" w Javie)
  2. Tak, można go pominąć.
  3. Chciałbym zadeklarować wirtualny destruktor, jeśli chcę, aby ta klasa została poddana podklasie, bez względu na to, czy podklasuje ona inną klasę, czy też nie, wolę też deklarować metody wirtualne, nawet jeśli nie są potrzebne. Dzięki temu podklasy będą działać, jeśli kiedykolwiek zdecydujesz się usunąć dziedziczenie. Ale myślę, że to tylko kwestia stylu.
+0

Destruktory nie są automatycznie wirtualne, podobnie jak żadne inne funkcje członkowskie. –

+1

@Neil; oczywiście nie, miałem na myśli _the_ destructor w tym przykładzie (tj. gdzie klasa podstawowa ma wirtualną), a nie ogólnie destruktory. Dotyczy to wszystkich metod, a nie tylko destruktorów. – falstro

0

1/Tak 2/Tak, to zostanie wygenerowany przez kompilator 3/Wybór między deklarowania virtual lub nie powinny stosować się do konwencji o nadpisane wirtualnych członków - IMHO, istnieją dobre argumenty zarówno sposób , po prostu wybierz jeden i wykonaj go.

Pominęłbym go, jeśli to możliwe, ale jest jedna rzecz, która może skłonić cię do zadeklarowania: jeśli użyjesz wygenerowanego kompilatora, to jest on niejawnie wbudowany. Są chwile, kiedy chcesz ominąć członków (na przykład biblioteki dynamiczne).

Powiązane problemy