O ile rozumiem, wprowadzono C ++ 14 std::make_unique
, ponieważ w wyniku nieokreślenia kolejności oceny parametrów było to niebezpieczne:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
(Wyjaśnienie: jeśli ocena najpierw przydzieli pamięć dla surowego wskaźnika, a następnie wywoła g()
i wyjątek zostanie zgłoszony przed std::unique_ptr
konstrukcją, wtedy nastąpi wyciek pamięci).
Dzwonienie std::make_unique
było sposobem na ograniczenie kolejności połączeń, dzięki czemu wszystko było bezpieczne:
f(std::make_unique<MyClass>(param), g()); // Syntax B
Od tego czasu C ++ 17 wyjaśnił kolejność oceny, czyniąc również składnię A bezpieczną, więc oto moje pytanie: czy nadal istnieje powód, aby używać konstruktora std::make_unique
over std::unique_ptr
w C ++ 17? Czy możesz podać kilka przykładów?
Na razie jedyny powód, jaki mogę sobie wyobrazić, to to, że pozwala na wpisanie MyClass
tylko raz (zakładając, że nie musisz polegać na polimorfizmie std::unique_ptr<Base>(new Derived(param))
). Jednak wydaje się to dość słabym powodem, zwłaszcza gdy std::make_unique
nie pozwala na określenie deleter, podczas gdy std::unique_ptr
konstruktor to robi.
Żeby było jasne, nie opowiadam się za usunięciem std::make_unique
z biblioteki standardowej (zachowanie tego ma sens przynajmniej ze względu na wsteczną kompatybilność), ale raczej zastanawiam się, czy nadal istnieją sytuacje, w których zdecydowanie preferowane jeststd::unique_ptr
źródło
std::unique_ptr
? To nie jest argument przeciwkomake_unique
std::make_unique
, myślę, że nie byłby to wystarczający powód, aby dodać go do STL, zwłaszcza gdy jest to składnia, która jest mniej wyrazista niż użycie konstruktora, nie więcejOdpowiedzi:
Masz rację, że główny powód został usunięty. Nadal istnieją nowe wytyczne dotyczące nieużywania nowych i mniej powodów związanych z pisaniem (nie trzeba powtarzać czcionki ani używać słowa
new
). Co prawda nie są to mocne argumenty, ale bardzo lubię nie widziećnew
w moim kodzie.Nie zapomnij też o konsystencji. Koniecznie powinieneś używać,
make_shared
więc używaniemake_unique
jest naturalne i pasuje do wzoru. Zmianastd::make_unique<MyClass>(param)
nastd::make_shared<MyClass>(param)
(lub odwrotnie) jest wtedy trywialna, gdzie składnia A wymaga znacznie więcej przepisania.źródło
new
, muszę się zatrzymać i pomyśleć: jak długo ten wskaźnik będzie żył? Czy poradziłem sobie z tym poprawnie? Jeśli jest wyjątek, czy wszystko zostało poprawnie wyczyszczone? Chciałbym nie zadawać sobie tych pytań i tracić na to czasu, a jeśli nie używamnew
, nie muszę zadawać tych pytań.new
. Czy to nie byłoby wspaniałe?std::make_shared
- wyobraź sobie przypadek, w którym przydzielony obiekt jest duży i jest na niego wielestd::weak_ptr
wskazujących: lepiej byłoby pozwolić, aby obiekt został usunięty zaraz po ostatnim udostępnieniu wskaźnik jest zniszczony i ma tylko mały wspólny obszar.std::make_shared
stackoverflow.com/a/20895705/8414561, gdzie pamięć użyta do przechowywania obiektu nie może zostać zwolniona, dopóki ostatniastd::weak_ptr
nie zniknie (nawet jeśli wszystkiestd::shared_ptr
wskazują na to (i w konsekwencji sam obiekt) zostały już zniszczone).make_unique
odróżniaT
odT[]
iT[N]
,unique_ptr(new ...)
nie.Możesz łatwo uzyskać niezdefiniowane zachowanie, przekazując wskaźnik, który został
new[]
ed do aunique_ptr<T>
, lub przekazując wskaźnik, który zostałnew
ed do aunique_ptr<T[]>
.źródło
Powodem jest krótszy kod bez duplikatów. Porównać
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); f(std::make_unique<MyClass>(param), g());
Można zaoszczędzić
MyClass
,new
i szelki. To kosztuje tylko jeden znak więcej w marki w porównaniu z PTR .źródło
MyClass
, ale zastanawiałem się, czy istnieje silniejszy powód, aby go używać<MyClass>
część z pierwszego wariantu.std::unique_ptr
gdy jest to niedozwolone. Ma to związek z rozróżnianiemstd::unique_ptr<T>
istd::unique_ptr<T[]>
Każde użycie
new
musi być bardzo dokładnie sprawdzone pod kątem poprawności przez cały okres użytkowania; czy zostanie usunięty? Tylko raz?Każde użycie
make_unique
nie dla tych dodatkowych cech; tak długo, jak obiekt będący właścicielem ma „prawidłowy” czas życia, rekurencyjnie powoduje, że unikalny wskaźnik ma „poprawny”.Otóż, prawdą jest, że
unique_ptr<Foo>(new Foo())
jest identyczny pod każdym względem od 1 domake_unique<Foo>()
; wymaga tylko prostszego "grep twojego kodu źródłowego do wszystkich zastosowań wnew
celu ich audytu".1 właściwie kłamstwo w ogólnym przypadku. Idealne przekazywanie nie jest idealne,
{}
domyślny init, tablice są wyjątkami.źródło
unique_ptr<Foo>(new Foo)
nie jest do końca identyczny zmake_unique<Foo>()
... to drugie tak.new Foo()
Ale poza tym tak.std::unique_ptr
gdy jest to niedozwolone. Ma to związek z rozróżnianiemstd::unique_ptr<T>
istd::unique_ptr<T[]>
To naprawdę nie wystarczy. Poleganie na niedawno wprowadzonej klauzuli technicznej jako gwarancji bezpieczeństwa nie jest bardzo solidną praktyką:
new
innym miejscu, np. Kopiując i wklejając swój przykład.new
gdzie indziej (ok, prawda, nie ma na to dużej szansy).Ogólnie rzecz biorąc, dobrym pomysłem jest, aby kod był odpowiedni / niezawodny / wyraźnie poprawny bez uciekania się do warstw językowych, wyszukiwania pomniejszych lub niejasnych klauzul technicznych w standardzie.
(jest to zasadniczo ten sam argument, który przedstawiłem tutaj na temat kolejności niszczenia krotek).
źródło
Rozważ void function (std :: unique_ptr (new A ()), std :: unique_ptr (new B ())) {...}
Załóżmy, że nowa A () się powiedzie, ale nowa B () zgłasza wyjątek: łapiesz ją, aby wznowić normalne wykonywanie programu. Niestety standard C ++ nie wymaga zniszczenia obiektu A i zwolnienia jego pamięci: pamięć po cichu przecieka i nie ma możliwości jej wyczyszczenia. Pakując A i B w std :: make_uniques, masz pewność, że wyciek nie wystąpi:
void function (std :: make_unique (), std :: make_unique ()) {...} Chodzi o to, że std :: make_unique i std :: make_unique są teraz obiektami tymczasowymi, a czyszczenie obiektów tymczasowych jest poprawnie określone w standard C ++: ich destruktory zostaną wyzwolone, a pamięć zwolniona. Więc jeśli możesz, zawsze wolisz przydzielać obiekty za pomocą std :: make_unique i std :: make_shared.
źródło