Różnice między std :: make_unique i std :: unique_ptr z new

130

Czy std::make_uniquema jakieś korzyści związane z wydajnością std::make_shared?

W porównaniu z tworzeniem ręcznym std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));
NFRCR
źródło
Czy make_sharedma jakąkolwiek skuteczność w porównaniu z samym pisaniem kodu długiej ręki?
Ed Heal,
9
@EdHeal Może, ponieważ make_sharedmoże przydzielić zarówno miejsce na obiekt, jak i miejsce na blok sterujący razem w jednej alokacji. Kosztem tego jest to, że obiekt nie może zostać zwolniony oddzielnie od bloku sterującego, więc jeśli używasz weak_ptrdużo, możesz skończyć z użyciem większej ilości pamięci.
bames53,
Być może to dobry początek stackoverflow.com/questions/9302296/ ...
Ed Heal

Odpowiedzi:

140

Motywacja make_uniquejest przede wszystkim dwojaka:

  • make_uniquejest bezpieczny do tworzenia tymczasowych, natomiast przy jawnym użyciu newnależy pamiętać o zasadzie nieużywania nienazwanych tymczasowych.

    foo(make_unique<T>(), make_unique<U>()); // exception safe
    
    foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*
  • Dodanie make_uniquewreszcie oznacza, że ​​możemy powiedzieć ludziom, aby „nigdy” nie używali, newa nie poprzednią regułę, aby „nigdy” nie używali, newz wyjątkiem sytuacji, gdy zrobisz unique_ptr".

Jest też trzeci powód:

  • make_uniquenie wymaga nadmiarowego użycia typu. unique_ptr<T>(new T())->make_unique<T>()

Żaden z powodów nie wiąże się z poprawą wydajności czasu wykonywania w sposób, w jaki używa się make_sharedtego (ze względu na unikanie drugiej alokacji, kosztem potencjalnie wyższego szczytowego wykorzystania pamięci).

* Oczekuje się, że C ++ 17 będzie zawierał zmianę reguły, która oznacza, że ​​nie jest to już niebezpieczne. Zobacz dokumenty komisji C ++ P0400R0 i P0145R3 .

bames53
źródło
Byłoby bardziej sensowne powiedzieć std::unique_ptri std::shared_ptrdlaczego możemy powiedzieć ludziom, aby „nigdy nie używali new”.
Timothy Shields,
2
@TimothyShields Tak, o to mi chodzi. Tyle, że w C ++ 11 mamy make_sharedi tak make_uniquejest ostatni element, którego wcześniej brakowało.
bames53,
1
Czy mógłbyś w jakikolwiek sposób wspomnieć krótko lub zamieścić link do powodu nieużywania nienazwanych tymczasowych?
Dan Nissenbaum
14
Właściwie od stackoverflow.com/a/19472607/368896 , mam to ... Od tej odpowiedzi, należy rozważyć następujące wywołanie funkcji f: f(unique_ptr<T>(new T), function_that_can_throw());- by zacytować odpowiedź: Kompilator jest dozwolony do rozmowy (w kolejności): new T, function_that_can_throw(), unique_ptr<T>(...). Oczywiście, jeśli function_that_can_throwfaktycznie rzuca, to wyciekasz. make_uniquezapobiega temu przypadkowi. Więc mam odpowiedź na moje pytanie.
Dan Nissenbaum
3
Jednym z powodów, dla których musiałem kiedyś użyć std :: unique_ptr <T> (new T ()), był fakt, że konstruktor T był prywatny. Nawet jeśli wywołanie std :: make_unique było w publicznej metodzie klasy T, nie zostało ono skompilowane, ponieważ jedna z podstawowych metod std :: make_unique nie mogła uzyskać dostępu do prywatnego konstruktora. Nie chciałem zaprzyjaźnić tej metody, ponieważ nie chciałem polegać na implementacji std :: make_unique. Więc jedynym rozwiązaniem było wywołanie new w mojej fabrycznej metodzie klasy T, a następnie zawinięcie jej w std :: unique_ptr <T>.
Patrick
14

std::make_uniquei std::make_sharedsą z dwóch powodów:

  1. Aby nie trzeba było jawnie wymieniać argumentów typu szablonu.
  2. Dodatkowe bezpieczeństwo wyjątków dotyczące używania std::unique_ptrlub std::shared_ptrkonstruktorów. (Zobacz sekcję Uwagi tutaj ).

Tak naprawdę nie chodzi o wydajność czasu wykonywania. Jest trochę o bloku kontrolnym i Tprzydzielaniu wszystkiego naraz, ale myślę, że to bardziej bonus, a mniej motywacja do istnienia tych funkcji.

Timothy Shields
źródło
Są tam również dla bezpieczeństwa wyjątków.
0x499602D2
@ 0x499602D2 I to dobry dodatek. Ta strona mówi o tym.
Timothy Shields
Dla przyszłych czytelników C ++ 17 nie pozwala na przeplatanie argumentów funkcji, więc argument dotyczący bezpieczeństwa wyjątków nie jest już dostępny. Alokacja pamięci dla dwóch równoległych std::make_sharedzapewni, że co najmniej jeden z nich zostanie zawinięty w inteligentny wskaźnik, zanim nastąpi alokacja innej pamięci, stąd nie ma wycieków.
MathBunny
7

Powód, dla którego musiałbyś użyć std::unique_ptr(new A())lub std::shared_ptr(new A())bezpośrednio zamiast std::make_*()nie jest w stanie uzyskać dostępu do konstruktora klasy Apoza bieżącym zakresem.

Volodymyr Lashko
źródło
0

Rozważ wywołanie funkcji

void function(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B())) { ... }

Załóżmy, że new się A()powiedzie, ale new B()rzuca wyjątek: łapiesz go, aby wznowić normalne wykonywanie programu. Niestety, standard C ++ nie wymaga, aby obiekt A został zniszczony, a jego pamięć zwolniona: pamięć po cichu przecieka i nie ma możliwości jej wyczyszczenia. Owijając A i B, std::make_uniquesmasz pewność, że wyciek nie nastąpi:

void function(std::make_unique<A>(), std::make_unique<B>()) { ... }

Chodzi o to, że std::make_unique<A>i std::make_unique<B>są teraz obiektami tymczasowymi, a czyszczenie obiektów tymczasowych jest poprawnie określone w standardzie C ++: ich destruktory zostaną wyzwolone, a pamięć zwolniona. Więc jeśli możesz, zawsze wolisz przydzielać obiekty za pomocą std::make_uniquei std::make_shared.

Dhanya Gopinath
źródło