Std :: make_unique ve std :: unique_ptr arasındaki farklar new ile


130

std::make_uniqueGibi herhangi bir verimlilik faydası var mı std::make_shared?

El ile oluşturmayla karşılaştırıldığında std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));

make_sharedSadece uzun el kodunu yazmaktan herhangi bir etkinliği var mı ?
Ed Heal

9
@EdHeal Bu olabilir, çünkü make_sharedhem nesne için alan hem de kontrol bloğu için alanı tek bir tahsis içinde birlikte tahsis edebilir. Bunun maliyeti, nesnenin kontrol bloğundan ayrı olarak ayrılamamasıdır, bu nedenle weak_ptrçok fazla kullanırsanız, daha fazla bellek kullanmanız gerekebilir.
bames53

Belki de bu iyi bir başlangıç ​​noktasıdır stackoverflow.com/questions/9302296/…
Ed Heal

Yanıtlar:


140

Arkasındaki motivasyon make_uniqueöncelikle iki yönlüdür:

  • make_uniquegeçiciler oluşturmak için güvenlidir, oysa açık kullanımda newisimsiz geçiciler kullanmama kuralını hatırlamanız gerekir.

    foo(make_unique<T>(), make_unique<U>()); // exception safe
    
    foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*
  • make_uniqueNihayetin eklenmesi, insanlara newönceki kural yerine "asla" kullanmamalarını "bir" yapmadığınız newsürece "asla" kullanmamalarını söyleyebileceğimiz anlamına gelir unique_ptr.

Üçüncü bir neden daha var:

  • make_uniqueyedekli tip kullanımı gerektirmez. unique_ptr<T>(new T())->make_unique<T>()

Sebeplerin hiçbiri, çalışma zamanı verimliliğini, kullanım şeklinin iyileştirilmesini make_sharediçermez (potansiyel olarak daha yüksek en yüksek bellek kullanımı pahasına ikinci bir ayırmadan kaçınılması nedeniyle).

* C ++ 17'nin artık güvenli olmadığı anlamına gelen bir kural değişikliği içermesi beklenmektedir. C ++ komite belgeleri P0400R0 ve P0145R3'e bakın .


Bunu söylemek daha mantıklı std::unique_ptrve std::shared_ptrbiz etmelerini söyleyebilir neden olan "hiçbir zaman kullanıma new."
Timothy Shields

2
@TimothyShields Evet, demek istediğim bu. Sadece C ++ 11'de bizde var make_sharedve make_uniquedaha önce eksik olan son parça da öyle.
bames53

1
Adsız geçiciler kullanmama nedeninizden kısaca bahsedebilir veya bağlantı kurabilirsiniz?
Dan Nissenbaum

14
Aslında gelen stackoverflow.com/a/19472607/368896 , ben ... Bu cevap aşağıdakilerden işlev çağrısını dikkate var f: f(unique_ptr<T>(new T), function_that_can_throw());-: cevabı alıntı (sırayla) derleyici çağrısına izin verilir: new T, function_that_can_throw(), unique_ptr<T>(...). Açıkçası, eğer function_that_can_throwgerçekten fırlatırsa, sızıntı yaparsınız. make_uniquebu durumu engeller. Yani sorum cevaplandı.
Dan Nissenbaum

3
Bir zamanlar std :: unique_ptr <T> (new T ()) kullanmamın bir nedeni, T'nin yapıcısının özel olmasıydı. Std :: make_unique çağrısı T sınıfının genel fabrika yönteminde olsa bile, std :: make_unique'in temel yöntemlerinden biri özel kurucuya erişemediği için derlenmedi. Bu yöntemi arkadaş yapmak istemedim çünkü std :: make_unique uygulamasına güvenmek istemedim. Yani tek çözüm, T sınıfının fabrika yöntemimde yeniyi çağırmak ve sonra onu bir std :: unique_ptr <T> içine sarmaktı.
Patrick

14

std::make_uniqueve std::make_sharediki nedenden dolayı var:

  1. Böylece, şablon türü bağımsız değişkenlerini açıkça listelemeniz gerekmez.
  2. std::unique_ptrVeya kullanımıyla ilgili ek istisna güvenliğistd::shared_ptr yapıcılar . ( Buradaki Notlar bölümüne bakın .)

Bu gerçekten çalışma zamanı verimliliği ile ilgili değil. Kontrol bloğu ve aynı Tanda tahsis edilmesiyle ilgili bir parça var , ancak bence bu daha çok bir bonus ve bu işlevlerin var olması için daha az motivasyon.


Ayrıca istisnai güvenlik için oradalar.
0x499602D2

@ 0x499602D2 Ve bu, iyi bir ek. Bu sayfa bundan bahsediyor.
Timothy Shields

Gelecekteki okuyucular için, C ++ 17 fonksiyon argümanlarının serpiştirilmesine izin vermez, bu nedenle istisna-güvenlik argümanı artık geçerli değildir. İki paralel için bellek tahsisi std::make_shared, diğer bellek tahsisi gerçekleşmeden en az birinin akıllı işaretçiye sarılmasını, dolayısıyla sızıntı olmamasını sağlayacaktır.
MathBunny

7

Kullanmak zorunda olmanızın std::unique_ptr(new A())veya std::shared_ptr(new A())doğrudan kullanmak zorunda olmanızın bir nedeni std::make_*(), sınıf yapıcısına Amevcut kapsam dışında erişememektir .


0

İşlev çağrısını düşünün

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

Yeninin A()başarılı olduğunu, ancak yeninin B()bir istisna attığını varsayalım : onu programınızın normal çalışmasını sürdürmek için yakalarsınız. Ne yazık ki, C ++ standardı, A nesnesinin yok edilmesini ve belleğinin serbest bırakılmasını gerektirmez: bellek sessizce sızar ve onu temizlemenin bir yolu yoktur. A ve B'yi içine sararak std::make_uniquessızıntının meydana gelmeyeceğinden emin olursunuz:

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

Buradaki nokta şu ki std::make_unique<A>ve std::make_unique<B>artık geçici nesnelerdir ve geçici nesnelerin temizlenmesi C ++ standardında doğru bir şekilde belirtilmiştir: yıkıcıları tetiklenecek ve bellek serbest bırakılacaktır. Dolayısıyla, eğer yapabiliyorsanız, her zaman nesneleri std::make_uniqueve kullanarak ayırmayı tercih edin std::make_shared.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.