2009-07-13 13 views
39

Czy domyślny destruktor może zostać wygenerowany automatycznie jako destruktor wirtualny?Czy domyślny destruktor może zostać wygenerowany automatycznie jako destruktor wirtualny?

Jeśli zdefiniuję klasę podstawową, ale brak domyślnego destruktora, czy domyślnie wygenerowany zostanie wirtualny destruktor ?

+0

przy okazji, tylko zastanawiasz się, jaki jest domyślny destrutor? czy jest więcej niż jeden rodzaj destruktora? – user88637

+4

@ yossi1981: jeśli nie zadeklarujesz destruktora w klasie, kompilator wstawi jeden dla ciebie. Ryzykując pomyłkę w niecodziennym przypadku, ten "domyślny destruktor" jest taki sam, jak gdybyś zdefiniował "~ MyClass() {}". –

+3

@onebyone: to be precise: 'public: ~ MyClass() {}' - nawet jeśli członkowie klasy są domyślnie prywatni. – MSalters

Odpowiedz

37

Nie. Koszt jest związany z wykonaniem metody wirtualnej, a C++ ma filozofię, która nie powoduje, że płacisz za rzeczy, których nie określasz wprost, których chcesz użyć. Jeśli wirtualny destruktor zostałby wygenerowany automatycznie, zapłaciłbyś cenę automatycznie.

Dlaczego po prostu nie zdefiniować pustego wirtualnego destruktora?

+42

Zauważ, że możesz także zrobić 'virtual ~ Foo() = default;' od [C++ 11] (http://en.cppreference.com/w/cpp/language/member_functions#Special_member_functions) (jeśli ktokolwiek jest czytając to od niedawna) – CoryKramer

+1

Jaki dodatkowy koszt może być, jeśli klasa ma w ogóle jakiekolwiek inne funkcje wirtualne? Czy mógłbyś podać przykład? – Spencer

+0

@ Spencer: Wywołanie destruktora wymaga adresu kodu d-tor, który zostanie rozwiązany w czasie wykonywania za pomocą tabeli vtable obiektu. Dla nie-wirtualnych destruktorów adres kodu jest znany w czasie kompilacji. –

1

Nie. Musisz zadeklarować to jako wirtualne.

8

Uri i Michael mają rację - dodam tylko, że jeśli to, co cię gryzie się konieczności dotykania dwa pliki zadeklarować i zdefiniować destruktora, to zupełnie w porządku, aby zdefiniować minimalny jeden inline w nagłówku:

class MyClass 
{ 
    // define basic destructor right here 
    virtual ~MyClass(){} 

    // but these functions can be defined in a different file 
    void FuncA(); 
    int FuncB(int etc); 
} 
+0

Właściwie sądzę, że przekonasz się, że gdy połączysz to, otrzymasz niezdefiniowane odniesienie do vtable MyClass. – keraba

+2

Otrzymasz tylko błąd "niezdefiniowane odniesienie do vtable", jeśli używasz GCC i nie definiujesz FuncA i FuncB bez linii, i to tylko dlatego, że GCC nie udało się wysłać wszystkich niezbędnych rzeczy, aby umożliwić prawidłowe linkowanie. –

+0

Jestem zaskoczony tym, dlaczego spowodowałoby to problem z łączeniem? Czy ktoś może rozwinąć? – Uri

9

Nie, wszystkie destruktory są domyślnie NIE wirtualne.

Trzeba będzie zdefiniować wirtualny destruktor na wszystkich klas bazowych

Poza tym.

Cytując Scott Meyers w swojej książce „Efektywne C++”:

The C++ language standard is unusually clear on this topic. When you try to delete a derived class object through a base class pointer and the base class has a non-virtual destructor (as EnemyTarget does), the results are undefined

W praktyce, jest to zwykle dobry pomysł, aby zdefiniować klasę z wirtualnego destruktora jeśli myślisz, że ktoś w końcu utworzyć klasę pochodną z tego. Mam tendencję do tego, aby wszystkie klasy miały wirtualny destruktor. Tak, wiąże się to z kosztem, ale kosztem tego, że nie jest on coraz częściej wirtualny, nie waży się marnego nakładu czasu pracy.

Proponuję tylko uczynić z niego nie-wirtualny, gdy jesteś absolutnie pewien, że chcesz go w ten sposób, a nie polegać na domyślnym, nie wirtualnym, który kompiluje wymuszenie. Możecie się nie zgadzać, jednak (podsumowując) Ostatnio miałem okropny wyciek pamięci na jakiś staroświecki kod, w którym wszystko, co zrobiłem, to dodanie std :: vector do jednej z klas, które istniały od kilku lat. Okazuje się, że jedna z jego klas bazowych nie ma zdefiniowanego destruktora (domyślny destruktor jest pusty, nie-wirtualny!) I jak żadna pamięć nie została przydzielona w ten sposób, zanim nie wycieknie do tego momentu. Wiele dni badań i czasu zmarnowane później ...

+1

Więc jeśli dobrze rozumiem sytuację, twój kod miał już niezdefiniowane zachowanie (chociaż nie wyciek pamięci) przed dokonaniem zmiany: usunięcie przez wskaźnik klasy bazowej, otrzymany obiekt dostaje zniszczoną bazę bez (pierwszego) samego zniszczenia . Brak innych elementów danych (które wymagałoby zniszczenia) nie powoduje zdefiniowania zachowania. Więc twoje śledztwo nie było zmarnowanym wysiłkiem ... –

9

Tak, dziedzicząc z klasy bazowej z wirtualnym destruktorem. W takim przypadku zapłacisz już cenę za klasę polimorficzną (np. Vtable).

43

w C++ 11 można użyć:

class MyClass 
{ 
    // create a virtual, default destructor 
    virtual ~MyClass() = default; 
}; 
+0

To kompiluje się z '' icpc'', ale nie '' g ++ - 4.6.3'', i nadaje komunikat '' error: ~virtual MyClass :: ~ MyClass() wirtualny nie może być domyślny w treści klasy ". Wygląda całkiem wyraźnie, że nie chcą, abyś to zrobił; czy istnieje wersja '' g ++ '', z którą to działa? – user14717

+1

@NickThompson, użyłem tego z gcc 4.8.1 i 4.9.0 pomyślnie. Wygląda na to, że jest to obsługiwane w wersji 4.6] (https://gcc.gnu.org/gcc-4.6/cxx0x_status.html). Czy kompilujesz z '-std = C++ 0x'? –

+0

Byłem; nie jestem pewien, co się tam dzieje. – user14717

2

Obecnie Uri ma rację. Z drugiej strony, po zadeklarowaniu wirtualnej metody w klasie, i tak płacisz cenę za istnienie wirtualnego stołu. W rzeczywistości kompilator ostrzeże Cię, jeśli twoja klasa ma metodę wirtualną, ale nie ma wirtualnego destruktora. Może to stać się kandydatem do automatycznego generowania domyślnego destruktora wirtualnego zamiast brzydkiego ostrzeżenia.

Powiązane problemy