C ++ 17'de neden std :: make_unique kullanılıyor?


98

Anladığım kadarıyla, C ++ 14 tanıtıldı std::make_uniqueçünkü parametre değerlendirme sırasının belirtilmemesi nedeniyle bu güvenli değildi:

f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A

(Açıklama: Eğer değerlendirme önce hafızayı ham işaretçi için ayırırsa, sonra çağırır g()ve yapımdan önce bir istisna atılırsa std::unique_ptr, hafıza sızdırılır.)

Arama std::make_unique, arama sırasını kısıtlamanın bir yoluydu ve böylece işleri güvenli hale getiriyordu:

f(std::make_unique<MyClass>(param), g());             // Syntax B

O zamandan beri, C ++ 17 değerlendirme sırasını netleştirdi ve Sözdizimi A'yı da güvenli hale getirdi, işte sorum şu: C ++ 17'de over yapıcısını kullanmak için hala bir neden var mı? Bazı örnekler verebilir misiniz?std::make_uniquestd::unique_ptr

Şu an için, hayal edebildiğim tek neden, MyClassyalnızca bir kez yazmaya izin vermesidir (polimorfizme güvenmeniz gerekmediğini varsayarak std::unique_ptr<Base>(new Derived(param))). Bununla birlikte, bu oldukça zayıf bir neden gibi görünüyor, özellikle std::make_uniquekurucunun yaptığı sırada bir silici belirtmeye izin std::unique_ptrvermediğinde.

Ve daha açık olmak std::make_uniquegerekirse, Standart Kitaplık'tan çıkarılmayı savunmuyorum (en azından geriye dönük uyumluluk için mantıklıdır), daha ziyade hala şiddetle tercih edilenstd::unique_ptr


4
Ancak, bu oldukça zayıf bir neden gibi görünüyor -> Neden zayıf bir neden? Türdeki kod çoğaltmasını etkili bir şekilde azaltır. Siliciye gelince, kullandığınızda özel bir siliciyi ne sıklıkla kullanıyorsunuz std::unique_ptr? Karşı make_unique
llllllllll

2
Bunun zayıf bir neden olduğunu söylüyorum çünkü std::make_uniqueilk etapta hayır yoksa, bunu STL'ye eklemek için yeterli bir neden olacağını sanmıyorum, özellikle kurucuyu kullanmaktan daha az anlamlı olan bir sözdizimi olduğunda, daha fazla değil
Sonsuz

1
Make_unique kullanarak c ++ 14'te oluşturulmuş bir programınız varsa, işlevin stl'den kaldırılmasını istemezsiniz. Veya geriye dönük olarak uyumlu olmasını istiyorsanız.
Serge

2
@Serge Bu iyi bir nokta, ancak sorumun amacının biraz dışında. Daha net hale getirmek için bir düzenleme yapacağım
Sonsuz

1
@Eternal, yanlış olduğu ve kafa karışıklığı yarattığı için lütfen C ++ Standart Kitaplığa STL olarak atıfta bulunmayı bırakın. Stackoverflow.com/questions/5205491/… sayfasına
Marandil

Yanıtlar:


77

Ana nedenin kaldırıldığı konusunda haklısın. Hala yeni yönergeler kullanmama ve daha az yazma nedenleri var (türü tekrarlamak veya sözcüğü kullanmak zorunda değilsiniz new). Kuşkusuz bunlar güçlü argümanlar değil ama kodumda görmemeyi gerçekten seviyorum new.

Ayrıca tutarlılığı da unutmayın. Kesinlikle make_sharedkullanmalısınız make_unique, bu yüzden kullanmak doğaldır ve kalıba uyar. Bu değişime, sonra Önemsiz std::make_unique<MyClass>(param)için std::make_shared<MyClass>(param)(ya da tersine) sözdizimi bir çok daha fazla yeniden yazma gerektirir.


44
@reggaeguitar Bir newdurup düşünmem gerektiğini görürsem : bu işaretçi ne kadar yaşayacak? Doğru mu hallettim? Bir istisna varsa, her şey doğru bir şekilde temizlendi mi? Kendime bu soruları sormamak ve bununla zamanımı boşa harcamak istemiyorum ve kullanmazsam newbu soruları sormak zorunda değilim.
NathanOliver

7
Projenizin tüm kaynak dosyaları üzerinde bir grep yaptığınızı ve tek bir tane bulamadığınızı hayal edin new. Bu harika olmaz mıydı?
Sebastian Mach

5
"Yeni kullanma" kılavuzunun temel avantajı basit olmasıdır, bu nedenle birlikte çalıştığınız daha az deneyimli geliştiricilere vermeniz kolay bir kılavuzdur. İlk başta fark etmemiştim ama bunun başlı başına bir değeri var
Ebedi

@NathanOliver Aslında kesinlikle kullanmak zorunda değilsiniz std::make_shared- tahsis edilen nesnenin büyük olduğu ve std::weak_ptrona işaret eden çok sayıda -s olduğu bir durumu hayal edin: nesnenin en son paylaşılır paylaşılmaz silinmesine izin vermek daha iyi olur işaretçi yok edildi ve sadece küçük bir ortak alanla yaşıyor.
Dev Null

1
@NathanOliver yapmazdın. Bahsettiğim şey, nesneyi depolamak için kullanılan belleğin sonuncusuna kadar serbest bırakılamayacağı std::make_shared stackoverflow.com/a/20895705/8414561 dezavantajıdır ( std::weak_ptrall std::shared_ptr-s onu işaret etse bile (ve sonuç olarak nesnenin kendisi zaten yok edildi).
Dev Null

53

make_uniqueayıran Tgelen T[]ve T[N], unique_ptr(new ...)yapmaz.

Kolayca bir işaretçi geçirerek tanımlanmamış davranışı elde edebilirsiniz new[]bir etmek ed unique_ptr<T>veya bir işaretçi geçirerek newbir karşı ed unique_ptr<T[]>.


Daha da kötüsü: Sadece değil, tamamen yapamaz.
Tekilleştirici

22

Bunun nedeni, kopyalar olmadan daha kısa koda sahip olmaktır. Karşılaştırmak

f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());

Kaydetmek MyClass, newve takviyeler. Sadece bir karakter daha fazlasında maliyeti marka ile karşılaştırıldığında ptr .


2
Şey, soruda da söylediğim gibi, sadece bir kez bahsetmekle daha az yazı yazdığını görebiliyorum MyClass, ancak kullanmak için daha güçlü bir neden olup olmadığını merak ediyordum
Ebedi

2
Çoğu durumda kesinti kılavuzu <MyClass>, birinci değişkendeki parçayı ortadan kaldırmaya yardımcı olur .
AnT

9
Diğer cevaplar için yorumlarda zaten söylendi, ancak c ++ 17, std::unique_ptrizin verilmemesi durumunda kurucular için şablon türü çıkarımı getirirken . Ayırt edici std::unique_ptr<T>vestd::unique_ptr<T[]>
Ebedi

19

Her kullanım, newömür boyu doğruluk için ekstra dikkatle denetlenmelidir; silinir mi? Sadece bir kere?

Her kullanımda make_uniquebu ekstra özellikler için değil; sahip olan nesne "doğru" yaşam süresine sahip olduğu sürece, benzersiz işaretçinin "doğru" olmasını özyinelemeli olarak yapar.

Şimdi, bunun unique_ptr<Foo>(new Foo())her yönden 1 ile aynı olduğu doğrudur make_unique<Foo>(); sadece daha basit bir " newonları denetlemek için tüm kullanımları için kaynak kodunuzu grep" gerektirir .


1 aslında genel durumda bir yalan. Mükemmel yönlendirme mükemmel değildir {}, varsayılan init, dizilerin hepsi istisnadır.


Teknik unique_ptr<Foo>(new Foo)olarak tam olarak aynı değil make_unique<Foo>()... ikincisi yapar new Foo()Ama başka türlü, evet.
Barry

@barry true, aşırı yüklenmiş operatör yeni mümkündür.
Yakk - Adam Nevraumont

@dedup C ++ 17 büyücülüğü ne faul?
Yakk - Adam Nevraumont

2
@Deduplicator, std::unique_ptrizin verilmemesi durumunda, c ++ 17 oluşturucular için şablon türü kesintisi sundu . Ayırt edici std::unique_ptr<T>vestd::unique_ptr<T[]>
Sonsuz

@ Yakk-AdamNevraumont Yeni aşırı yüklemeyi kastetmedim, sadece varsayılan-init ile değer-init'i kastetmiştim.
Barry

0

O zamandan beri, C ++ 17 değerlendirme sırasını netleştirerek Söz Dizimi A'yı da güvenli hale getirdi

Bu gerçekten yeterince iyi değil. Güvenlik garantisi olarak yeni tanıtılan bir teknik maddeye güvenmek çok sağlam bir uygulama değildir:

  • Birisi bu kodu C ++ 14'te derleyebilir.
  • Çiğ kullanımını teşvik edeceksiniz newÖrneğin, örneğinizi kopyalayıp yapıştırarak, çiğlerin başka yerlerde edebilirsiniz.
  • SM'nin önerdiği gibi, kod kopyası olduğu için bir tür, diğeri değiştirilmeden değiştirilebilir.
  • Bir tür otomatik IDE yeniden düzenleme bunu newbaşka bir yere taşıyabilir (tamam, verilmiş, pek şansı yok).

Genellikle, bu kod açıkça geçerli / sağlam / uygun olması için iyi bir fikir olmadan standardında teknik maddeleri, dil-laywering başvurmadan minör ararken veya gizleyebilir.

(Bu, esasen burada demet yıkımının sırası hakkında yaptığım argümanla aynı .)


-1

Void işlevini düşünün (std :: unique_ptr (new A ()), std :: unique_ptr (new B ())) {...}

Yeni A () 'nın başarılı olduğunu, ancak yeni B ()' nin 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 std :: make_uniques içine sararak sızıntının gerçekleşmeyeceğinden emin olursunuz:

void işlevi (std :: make_unique (), std :: make_unique ()) {...} Buradaki nokta şu ki std :: make_unique ve std :: make_unique artık geçici nesnelerdir ve geçici nesnelerin temizlenmesi C ++ standardı: yıkıcıları tetiklenecek ve bellek serbest bırakılacaktır. Dolayısıyla, eğer yapabiliyorsanız, nesneleri her zaman std :: make_unique ve std :: make_shared kullanarak tahsis etmeyi tercih edin.


4
Yazar, C ++ 17'de artık sızıntıların olmayacağını soruda açıkça belirtmiştir. "O zamandan beri, C ++ 17 değerlendirme sırasını netleştirdi ve Sözdizimi A'yı da güvenli hale getirdi, işte sorum şu: (...)". Sorusuna cevap vermedin.
R2RT
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.