2012-03-28 7 views
7

Zawsze słyszałem, że nie powinieneś dziedziczyć z klasy bez wirtualnych destruktorów, a ja nie zwracałem na to uwagi, ponieważ po prostu nie używam dziedziczenia tak często. Czy ta reguła ma zastosowanie, nawet jeśli nie chcesz używać polimorfizmu, ale chcesz tylko całej klasy i chcesz dodać więcej? Żeby być konkretnym, czy następująca klasa byłaby bezpieczna, o dobrze określonym zachowaniu, o ile nie używałbym jej polimorficznie? (to znaczy nie ma Usuwanie bazowe wskaźniki do obiektów pochodzących)Dziedziczenie z klas bez destruktorów wirtualnych

template<typename T> 
class SomewhatSafeVector : public std::vector<T> 
{ 
public: 
    typedef std::vector<T> base; 

    T& operator[](unsigned n) { 
     if (n >= base::size()) 
     { 
      throw IndexOutOfBounds(); 
     } 
     return base::operator[](n); 
    } 
}; 
+1

Nieważne, czy jest OK, czy nie, ale nadal nie powinieneś czerpać ze standardowych kontenerów bibliotecznych. Co więcej, jeśli masz kłopoty z dostępem do dynamicznych kontenerów w ich granicach, możesz raczej spojrzeć na swoje myślenie algorytmiczne z dużym obrazem (pomyśl "0-1-wiele" i "zakresy"), ponieważ niezwiązany dostęp jest zwykle błąd * logiki *. –

+4

Myślę, że w twoim konkretnym przykładzie, dziedziczenie nie jest strasznie eleganckim rozwiązaniem, ponieważ dziedziczenie jest przeznaczone do ponownego użycia interfejsu, a nie ponownego użycia. Oczywiście nie korzystasz ponownie z interfejsu, ponieważ 'operator []' zgłasza wyjątek, którego 'std :: vector' nie będzie. Jeśli chcesz ponownie użyć kodu, użyj prostych funkcji współdzielonych lub (jak w tym przypadku), przekształć 'std :: vector' w element' SomewhatSafeVector'. –

+0

@KerrekSB: Po pierwsze, dlaczego nie? Po drugie, nie mam takich kłopotów. Sądzę jednak, że sprawdzone kontenery są dobrym pomysłem do celów instruktażowych i debugowania. –

Odpowiedz

6

Zawsze słyszałem, że nie powinien dziedziczyć z klasy bez destruktorów wirtualnych

Jest to zasada podana do początkujących, ponieważ wyjaśniające wszystkie zawiłości zbyt dużo czasu i jest po prostu bezpieczniejsze (i nie tyle kosztowne dla programów ćwiczeń), aby dać im tylko kilka linii bazowych, które działają cały czas (choć może to być przesada).

Możesz doskonale dziedziczyć bez destruktora virtual w klasie bazowej. Z drugiej strony, jeśli klasa bazowa w ogóle nie ma metody, dziedziczenie jest prawdopodobnie niewłaściwym narzędziem do zadania. Na przykład: w twoim przypadku, jeśli użyję SafeVector<T> sv; sv[3];, to jest to bezpieczne, jednak jeśli zrobię std::vector<T>& v = sv; v[3]; to nie jest ... to dlatego, że jesteś tylko ukrywanie metody klasy bazowej, nie przesłaniając go (wyskakuj ostrzeżenie poziom, dadzą ci znać).

Właściwym sposobem byłoby użycie kompozycji, a następnie utworzenie metod przesyłania do elementu implementacji dla metod, których naprawdę używasz. W praktyce robi się męczący, ponieważ C++ nie obsługuje delegowania (using attribute.insert;) tak wielu ucieka się do dziedziczenia ...

Inną alternatywą jest zapewnienie bezpieczniejszych metod jako bezpłatnych metod, ponieważ zawsze możesz dodać bezpłatne metody bez ograniczeń. Może się wydawać mniej idiotyczne dla osób o nastawieniu "OO", a niektórzy operatorzy nie mogą być tak dodani.

5

Jeśli nie zamierzasz używać polimorficznie klasy (bez Wskaźniki bazowe do usuwania obiektów pochodnych) to nie jest niezdefiniowane zachowanie.

odniesienia:

C++ 03 standard: 5.3.5 Usuwanie

5.3.5/1:

Operator usuwania ekspresja niszczy większość przedmiotów pochodzących (1.8) lub tablica utworzona przez nowe wyrażenie.
usunąć wyrażenie:
:: zdecydować usunąć oddanych wyrażenie
:: zdecydować delete [] cast-ekspresja

5.3.5/3:

W pierwszym alternatywnie (usuń obiekt), jeśli typ statyczny argumentu jest różny od jego typu dynamicznego, typ statyczny będzie klasą podstawową dynamicznego typu argumentu, a typ statyczny będzie miał wirtualny destruktor lub zachowanie będzie niezdefiniowane. W drugiej alternatywie (usunąć tablicę) jeżeli dynamiczny rodzaj obiektu, który ma zostać usunięty różni się od jego typu statycznego, zachowanie jest undefined.73)

4

Zapraszamy do korzystania z tego obiektu polimorficznie, po prostu może 't delete to polimorficznie. Jeśli nie usuniesz wskaźników do obiektów swojej klasy przez std::vector<>*, jesteś bezpieczny.

Poza: Można uprościć operator[] tak:

T& operator[](unsigned n) { return this->at(n); } 
2

Tak, jeśli nie używasz polimorfizm (czyli nigdy uskok odniesienie lub wskaźnik), nie ma sposobu, aby wykonać niebezpieczną zniszczenie .

Klasy Mixin są często używane w ten sposób, a CRTP rzadko implikuje wirtualny destruktor, aby nazwać wzorce pary.

Powiązane problemy