2011-11-09 18 views
6

budowa dwufazowy przyjmuje następującą postać:Czy inteligentne wskaźniki wykluczają potrzebę budowy dwufazowej?

struct something { 
    something() 
     : p1(NULL) 
     , p2(NULL) 
    { } 

    ~something() { 
     if (p1) delete p1; 
     if (p2) delete p2; 
    } 

    void initialize() { 
     p1 = new int(2); 
     p2 = new int(5); // May throw if allocation fails! 
    } 

    int* p1; 
    int* p2; 
}; 

punktu, który jest, że naiwny konstruktor (który nie oglądać za niepowodzenia alokacji) będzie przeciekać pamięć: destructor częściowo zbudowane obiektu nigdy nie jest nazywany .

Moje pytanie: czy poniższy kod jest bezpieczny, a przez corollary, czy inteligentne wskaźniki zapobiegają dwufazowej konstrukcji?

struct something { 
    something() 
     : p1(new int(2)) 
     , p2(new int(5)) 
    { } 

    std::unique_ptr<int> p1; 
    std::unique_ptr<int> p2; 
}; 
+6

Nie potrzebujesz warunku przed 'delete'. Poważnie, nie masz. Usuwanie wartości zerowej jest całkowicie w porządku. –

+0

Są też nowe i niepotrzebne skasowania, które działają z alokacją pamięci podobnie jak malloc()/free(). Ale nawet jeśli sama alokacja nie zakończy się niepowodzeniem przy użyciu nowych/niezawierających rzutów, konstruktorzy obiektów mogą nadal rzucać. –

+0

@KerrekSB Noted. :) –

Odpowiedz

5

Tak, twój nowy kod jest w porządku. Należy jednak pamiętać, że nie jest możliwe subtelnie w bardziej złożonych przypadkach:

#include <memory> 

struct foo { 
    foo(std::shared_ptr<int> a, std::shared_ptr<int> b) { } 
}; 

struct bar { 
    foo f; 
    bar() : f(std::shared_ptr<int>(new int), std::shared_ptr<int>(new int)) { } 
}; 

int main() { 
    bar b; 
} 

nie będzie bezpieczny, ponieważ jednak kolejność oceny argumentów konstruktora foo na liście inicjatora z bar jest nieokreślona. Kompatybilny kompilator może zdecydować się na wykonanie pierwszego rzędu oceny (lub cokolwiek innego, o ile wszystkie zostały poprawnie ocenione na końcu). Oznacza to, że jeśli pierwszy new int powiodł się, ale drugi wyrzucił, zanim obiekty shared_ptr zostały skonstruowane, pierwsza alokacja do wykonania mogła nadal przeciekać.

Jeśli chcesz tego dokonać, istnieją dwa możliwe rozwiązania, poza tym, że po prostu wracają do dwufazowej konstrukcji: pierwsza może być refaktorem, druga to najpierw konstrukcja shared_ptr s jako elementy paska , przed f. To, które z nich jest najbardziej odpowiednie, to wezwanie do sądu, które moim zdaniem musi być podejmowane indywidualnie dla każdego przypadku.

5

Moje pytanie: czy jest bezpieczny poniższy kod,

Tak, to byłoby OK.

struct something { 
    something() 
     : p(new int(5)) 
    { } 

    std::unique_ptr<int> p; 
}; 

Należy pamiętać, że kod naiwny

struct something { 
    something() 
     : p(new int(5)) 
    { } 

    int* p; 
}; 

byłby bezpieczny wyjątku, także dlatego, że jest tylko jedno przeznaczenie, które może zakończyć się niepowodzeniem. Myślę, że mówisz raczej o tym, że nie chcesz tego zrobić. Inteligentne wskaźniki również działałyby w tym przypadku.

+0

Tak, masz całkowitą rację; Zaktualizowałem przykład, aby rzeczywiście spowodować wyciek. –

0

W ogóle nie potrzebujesz dwufazowej konstrukcji, jeśli po prostu poradzisz sobie z wyjątkiem. To jest sposób RIAA.

struct something { 
    something() 
     : p1(NULL) 
     , p2(NULL) 
    { 
     p1 = new int(2); 
     try { 
      p2 = new int(5); // May throw if allocation fails! 
     } catch (std::bad_alloc&) { 
      delete p1; //cleanup 
      throw; //rethrow 
     } 
    } 

    ~something() { 
     delete p1; 
     delete p2; 
    } 

    int* p1; 
    int* p2; 
}; 
+0

Jeśli się nie mylę, jest to oczywiście ** nie ** droga RAII, ponieważ najpierw ** zainicjujesz ** 'p1' z' NULL', i ** później przydzieli ** 'nowe int (2) '. – bitmask

+0

To sprawia, że ​​'coś' spełnia RAII, chociaż jego elementy wewnętrzne nie spełniają RIAA.Kiedy zagłębisz się wystarczająco do wnętrza jakiejkolwiek klasy RIAA, w końcu dotrzesz do klasy, która nie jest zgodna z RIAA. Chodzi mi o wyjątek bezpieczeństwa i brak dwuetapowej inicjacji. 'unique_ptr' również nie ma członków RIAA. –

+0

Rozumiem. Przepraszam. :) – bitmask

Powiązane problemy