2010-10-14 13 views
6

W moim destruktorze muszę wyczyścić kilka zasobów. Powiedzmy, że mam trzy połączenia, aby wyczyścić zasoby, które mogą rzucić. Ponieważ nie jest dobrze, aby pozwolić wyjątkowi na opuszczenie destruktora, jaki powinien być mój wzorzec projektowy? Podobno droga poniżej nie jest skalowalna.Wyjątek w destruktorze

Dzięki.

class B::~B(){ 

try{ 
    clearResourceA() 
} 
catch{ 
    try{ 
     clearResourceB(); 
     } 
    catch{ 
     clearResourceC(); 
    } 
    clearResourceC(); 
} 
clearResourceB(); 
    . 
    . 
} 
+0

Jest * zdecydowanie * nie jest skalowalny. Próbując zarządzać tylko trzema zasobami, masz już błędy logiczne. – nobar

Odpowiedz

2

Przechwytywanie wszystko, co może rzucać w destructor z catch-all (tj catch (...)) i jak najlepiej obsłużyć wyjątki rzucane. Upewnij się, że żadne wyjątki nie rozprzestrzeniają się z destruktora, co pomaga w zapobieganiu.

10

Dlaczego nie:

try{clearResourceA();} catch(...){} 
try{clearResourceB();} catch(...){} 
try{clearResourceC();} catch(...){} 
0
try 
{ 
    ClearResourceA(); 
} 
catch(...) 
{ 
    ExceptionWasThrown(); 
} 
try 
{ 
    ClearResourceB(); 
} 
catch(...) 
{ 
    ExceptionWasThrown(); 
} 
... 
+0

Offtop: Jak mogę wymusić wcięcie? –

+0

Użyj przycisku "kod", a nie tyknięć. Przycisk kodu ma 101010 na nim. Wpisz swój kod (wcięty), podświetl kod, a następnie naciśnij przycisk kodu. – Starkey

+0

@Starkey: Niestety dzisiaj nie ma ani tych przycisków, ani podglądu. Zapytałem o meta, ale wciąż oczekująca odpowiedź: –

5

Kapsułkować każdy zasób w klasie, który podaje je w swoim destructor (z okolic try/catch):

struct ProperlyManagedA { 
    // some means of using the resource - a rudimentary way is this: 
    A &getA() { return a; } 
    const A &getA() const { return a; } 
    // cleanup 
    ~ProperlyManagedA() { 
     try { 
      a.clear(); // whatever it is ClearResourceA actually does 
     } catch (...) {} 
    } 
    private: 
    A a; 
} 

shared_ptr z niestandardowych deleter jest jednym ze sposobów osiągnięcia tego bez konieczności tworzenia całej klasy dla każdego rodzaju zasobów.

Możesz odrzucić wyjątek (np. Zareagować na problem) w zależności od tego, co zostało wygenerowane.

Zmodyfikuj zasoby A, B i C tak, aby oczyszczały się z własnych destruktorów. To może nie być możliwe.

Tak czy inaczej, możesz umieścić tyle zasobów w jednej klasie, ile chcesz, bez dodawania żadnego kodu do destruktora klasy. To jest "skalowalność". Cały punkt RAII polega na tym, że każdy użytkownik zasobu nie powinien zapisywać kodu oczyszczającego, aby prawidłowo używać zasobu.

+1

Dobrze sortowane. Celem RAII jest to, że oczyszczanie odbywa się w twoich destruktorach, więc nie wywołujesz delete ani freeHandle ani żadnych funkcji wewnątrz ciał twojego kodu. Główną kwestią jest to, że kod czyszczenia ogólnie nie powinien wrzucać w pierwszej kolejności. – CashCow

+0

@CashCow: "oczyszczanie odbywa się w twoich destruktorach" - cóż, jeśli zasoby są zaprojektowane z zasadami RAII, to czyszczenie odbywa się w destruktorze zasobu. Jeśli muszę sam napisać klasę RAII, to co prawda to mój destruktor. Ale ktokolwiek zapisuje zasób RAII, użytkownicy tego zasobu RAII nie muszą pisać ani jawnie wywoływać kodu oczyszczającego. To właśnie miałem na myśli przez "użytkowników". –

+0

dziękuję, szaleję patrząc na inne sugestie:/ –

2

Można również zawijać swoje funkcje ClearResources, aby upewnić się, że nigdy nie rzucają. Dlaczego i tak mogą wyrzucić?

+0

Typowy spłukiwanie (lub inne operacje wejścia/wyjścia) są zwykle powodem. –

1

Ponieważ pytasz o wzór wzór Powiem, z jakiej zasady używam z powodzeniem. Wszystkie funkcje, które mają funkcję czyszczenia/zakańczania, powinny odrzucić NIGDY.

Funkcje te są zwykle dobrze znane nazwy:

  • Przezroczysty *()
  • Clean *()
  • Zakończyć *()
  • Destroy *()
  • Release *()
  • Odłącz *()
  • Bezpłatnie *()
  • Erase *()
  • ~ Destructor()
  • ...

Uzasadnieniem który stoi za to, że:

  • musi przez co najmniej 1 zasób funkcji za które gwarancje że specyficzny zasób jest wyczyszczone
  • (rekurencyjnie), jeśli budować czyste- i zwolnij wiele zasobów, a następnie używaj tylko funkcji, które gwarantują , że te zasoby zostaną zwolnione w wyjątkowy sposób
  • programistów używających yo ur kod musi mieć drogę do oczyszczania zasobów
  • jeśli eksportować funkcje porządkowe na zewnątrz biblioteki to nie propagować wyjątek (ponieważ użytkownicy biblioteki nie będą wiedzieć, czy zasób jest zwolniony)

And I można spróbować użyć RAII pattern. Ale nawet w tym przypadku powyższe zasady będą w użyciu.

Powiązane problemy