2012-07-05 13 views
13

Funkcja Boost's make_shared() może być wyjątkowo bezpieczna podczas próby utworzenia shared_ptr.Dlaczego funkcja boost nie ma funkcji make_scoped()?

Dlaczego nie ma odpowiednika w postaci make_scoped()? Czy istnieje wspólna najlepsza praktyka?

Oto przykładowy kod z boost::scoped_ptr documentation że wydaje się niebezpieczne dla mnie:

boost::scoped_ptr<Shoe> x(new Shoe); 

Ta linia kodu zrobi te trzy rzeczy w celu:

  • przydzielić pamięci sterty dla Shoe
  • Zadzwoń do konstruktora pod numer Shoe
  • Wywołanie konstruktora dla boost::scoped_ptr<Shoe>

Jeżeli konstruktor Shoe zgłasza wyjątek, pamięć będzie przeciekał. (patrz odpowiedź R. Martinho Fernandesa)scoped_ptr nie zajmie się dealokacją, ponieważ nie została jeszcze zbudowana.

Czy to przeoczenie? Czy istnieje rozwiązanie, którego nie zauważyłem?

+0

Ten przykład jest bezpieczny, ale taki, który nie jest: 'f (boost :: scoped_ptr (new Shoe), g());'. Praktyka kodowania w celu rozwiązania problemu: Zawsze wymieniaj inteligentne wskaźniki jako zmienne lub elementy, nie twórz ich jako tymczasowych wyrażeń. – aschepler

Odpowiedz

13

Jeśli konstruktor ulegnie awarii, pamięć nie wycieknie. To część semantyki new żadne inteligentne wskaźniki zaangażowany:

struct Foo { Foo() { throw 23; } }; 
new Foo(); // no memory leaked 

Dodana bezpieczeństwo wyjątek przewidziany przez make_shared pochodzi od kiedy jesteś inicjowanie dwa shared_ptr s w wyrażeniu i dwa pliki uruchamiania nie są wyświetlane sekwencyjnie, jak to jest w przypadku argumentów funkcji telefonicznych:

struct Bar { 
    Bar(bool fail) { 
     if(fail) throw 17; 
    } 
} 
f(shared_ptr<Bar>(new Bar(true)), shared_ptr<Bar>(new Bar(false))); 

Ponieważ nie ma sekwencjonowanie między ocenach new Bar(true), shared_ptr<Bar>(new Bar(true)), new Bar(false) i shared_ptr<Bar>(new Bar(false)), może się zdarzyć:

  1. new Bar(false) jest obliczany i się powiódł: pamięć jest alokowana;
  2. new Bar(true) jest oceniany i kończy się niepowodzeniem: nie wycieka z pamięci w wyniku tej oceny;

No shared_ptr został skonstruowany w tym czasie, więc pamięć przydzielona w punkcie # 1 jest teraz wyciekła.

+0

Czy to brzmi tak, jak ja rozumiem? 'new' obiecuje wychwycić wyjątki wyrzucone z konstruktora, zwolnić pamięć, a następnie ponownie rzucić? –

+3

@Drew: Tak, coś takiego. Jeśli jesteś zainteresowany, jest to opisane w standardzie w §5.3.4 paragrah 18. –

+3

Dla kompletności innym celem 'make_shared' (w większości implementacji) jest to, że przydziela pamięć dla licznika referencyjnego w tym samym czasie, co czas utworzony obiekt (przydziela jeden blok wystarczająco duży dla obiektu i liczby), aby nie ponosić kary za wydajność, jeśli przydzielono dwie sterty. 'Scoped_ptr' (lub' unique_ptr' w C++ 11 nie wymaga licznika odwołań i dlatego nic z tego nie zyskuje w tym dziale. – John5342

1

Jeśli Rzut buta to But nie jest skonstruowany tak, że nic nie może zrobić naprawdę scoped_ptr. Nie? Plik scoped_ptr x jest na stosie i zostanie wyczyszczony na wyjściu lunety.

14

scoped_ptr poprzedza ruch semantyki i nie można go kopiować według projektu. Zatem niemożliwe byłoby wdrożenie make_scoped, ponieważ aby zwrócić obiekt z funkcji, jego typ musi być ruchomy lub możliwy do kopiowania.