std :: shared_ptr son çare olarak?


59

Sadece "Going Native 2012" yayınlarını izliyordum ve hakkında bir tartışma gördüm std::shared_ptr. Bjarne'nin olumsuz görüşlerini std::shared_ptrve bir nesnenin yaşam süresi belirsiz olduğunda (buna göre, nadiren bunun olması gerektiğine inanıyorum), bunun “son çare” olarak kullanılması gerektiği şeklindeki görüşünü duyduğuma biraz şaşırdım .

Bunu daha derinlemesine açıklamak isteyen var mı? std::shared_ptrNesneleri ömür boyu olmadan nasıl güvenli bir şekilde yönetebiliriz ?


8
İşaretçiler kullanmıyor musunuz? Nesnenin belirli bir sahibine sahip olmak, ömrünü yönetmek?
Bo Persson,

2
açıkça paylaşılan verilerden ne haber? İşaretçi kullanmamak zor. Ayrıca std :: shared_pointer bu durumda kirli "kullanım ömrünü idare eder" yapar
Kamil Klimek

6
Sunulan önerileri daha az dinlemeyi ve bu önerinin ardındaki tartışmayı daha fazla dinlemeyi düşündünüz mü? Bu tür bir tavsiyenin işe yarayacağı bir sistem türünü oldukça iyi açıklıyor.
Nicol Bolas,

@NicolBolas: Tavsiyeyi ve argümanı dinledim ama açıkçası yeterince iyi anladığımı bilmiyordum.
ronag

Saat kaçta "son çare" diyor? Bit'i 36. dakikada izleyen ( channel9.msdn.com/Events/GoingNative/GoingNative-2012/… ), işaretçilerin kullanımına karşı temkinli olduğunu ancak genel olarak işaretçiler anlamına geldiğini, yalnızca paylaşılan_ptr ve benzersiz_tr normal 'işaretçi. Nesnelerin kendilerinin (ve yeni ile tahsis edilen nesnelere işaretçiler değil) tercih edilmesi gerektiğini belirtir. Sunumda sonradan düşündüğünüz şey miydi?
Pharap

Yanıtlar:


55

Paylaşılan sahiplikten kaçınmanız durumunda, uygulamanız daha kolay ve kolay anlaşılır ve bu nedenle bakım sırasında ortaya çıkan hataları daha az duyarlıdır. Karmaşık veya net olmayan mülkiyet modelleri, uygulamanın farklı bölümlerinin bağlantılarını, kolayca izlenemeyen paylaşılan durumlarla takip etmekte zorlanma eğilimindedir.

Bu göz önüne alındığında, otomatik saklama süresi olan nesnelerin kullanılması ve "değer" alt nesnelere sahip olması tercih edilir. Bunu başaramamak, son çare olmasa da, arzulanan araçlar listesinden bir şekilde çıkmanın unique_ptriyi bir alternatifi olabilir shared_ptr.


5
Meselenin tekno kendisi olmadığını (ortak mülkiyet) değil, kendimiz için getirdiği zorlukları, sonra neler olup bittiğini deşifre etmek zorunda kalan insanlar olarak belirtmek için +1 .
Matthieu M.,

Bununla birlikte, böyle bir yaklaşımı benimsemek, bir programcının önemsiz OOP sınıflarının çoğuna eşzamanlılık programlama kalıpları uygulama yeteneğini ciddi şekilde sınırlayacaktır (kopyalanamazlık nedeniyle).
rwong

48

Bjarne'nin yaşadığı dünya ... daha iyi bir terim istemek için çok akademik. Kodunuz, nesnelerin çok kasıtlı ilişkisel hiyerarşilere sahip olacak şekilde tasarlanıp yapılandırılabilirse, sahiplik ilişkileri katı ve boyun eğmeyecek şekilde düzenlenirse, kod tek bir yönde akar (yüksek seviyeden düşük seviyeye) Hiyerarşi, o zaman fazla ihtiyaç bulamazsınız shared_ptr. Birisinin kuralları çiğnemek zorunda olduğu nadir durumlarda kullandığınız bir şey. Ancak, aksi takdirde, her şeyi yalnızca vectorveya anlam semantiği kullanan diğer veri yapılarına ve unique_ptrtek olarak ayırmanız gereken şeyler için yapıştırabilirsiniz .

Yaşamak için harika bir dünya olsa da, her zaman yapacakların bu değil. Eğer varsa edemez yapmak için çalışıyoruz sisteminin tasarımı imkansız (veya sadece derin tatsız) olduğu anlamına gelir, çünkü bu şekilde kodunuzu düzenlemek, o zaman kendinizi daha nesnelerin ortak sahipliğini ihtiyacı bulacaklardır .

Böyle bir sistemde, çıplak işaretçiler tutmak ... tam olarak tehlikeli değildir, ancak soruları da beraberinde getirir. Bunun en iyi yanı , nesnenin ömrü shared_ptrhakkında makul sözdizimsel garantiler sağlamasıdır. Kırılabilir mi? Tabii ki. Ancak insanlar da bir const_castşeyler yapabilir ; Temel bakım ve beslenme, shared_ptrmülkiyeti paylaşılması gereken tahsis edilmiş nesneler için makul bir yaşam kalitesi sağlamalıdır.

Sonra, weak_ptra yokluğunda kullanılamaz s vardır shared_ptr. Sisteminiz katı bir şekilde yapılandırılmışsa, bir uygulamanın yapısının işaret ettiği nesnenin sizi geride bırakmasını sağladığından emin olarak, bir nesneye çıplak bir işaretçi koyabilirsiniz. İşaretçiyi bazı iç veya dış değerlere döndüren bir işlevi çağırabilirsiniz (örneğin, X isimli nesneyi bulun). Düzgün bir şekilde yapılandırılmış kodda, bu işlev yalnızca nesnenin kullanım ömrünü kendinizin geçeceği garantilenmişse kullanılabilir olacaktır; bu nedenle, bu çıplak imleci nesnenizde saklamak iyidir.

Bu rijitlik gerçek sistemlerde her zaman elde edilemeyeceğinden , kullanım ömrünü makul şekilde güvence altına almak için bir yol gerekir . Bazen tam mülkiyete ihtiyaç duymazsınız; bazen, işaretçinin ne zaman kötü veya iyi olduğunu bilmeniz gerekir. İşte bu noktada weak_ptrgeliyor Ben durumlar olmuştur. Olabilirdi bir kullanmış unique_ptrOr boost::scoped_ptr, ama kullanmak zorunda kaldı shared_ptrçünkü özellikle birilerine "uçucu" işaretçi vermek gerekiyordu. Ömrü belli olan bir işaretçi belirsizdi ve bu işaretçi imha edildiğinde sorgu yapabilirlerdi.

Dünyadaki devlet belirsiz olduğunda hayatta kalmak için güvenli bir yol.

Bu, işaretçiyi almak yerine, işlev çağrısı tarafından yapılmış olabilir weak_ptrmi? Evet, ama bu daha kolay kırılabilir. Çıplak bir işaretçiyi döndüren bir fonksiyonun, söz konusu işaretçiyi uzun süreli saklamak gibi bir şey yapmamasını öneren sözdizimi açısından hiçbir yolu yoktur. shared_ptrAyrıca geri dönmek , birisinin basitçe saklamasını ve nesnenin ömrünü uzatmasını çok kolaylaştırır. weak_ptrBununla birlikte, geri döndüğünüzden emin shared_ptrolmanız sizi depolamanın lockşüpheli bir fikir olduğunu gösteriyor. Bunu yapmaktan alıkoyamaz, ama C ++ 'da hiçbir şey kodları kırmanızı engellemez. weak_ptrdoğal olanı yapmaktan minimum direnç sağlar.

Şimdi, bu fazla kullanılamaz demek shared_ptrdeğil ; kesinlikle yapabilir. Özellikle de, önceden kullandığım birçok durum vardı çünkü bir RAII işaretçisini etrafa geçirmem ya da listeye koymam gerekti. Ve hareket semantik olmadan , tek gerçek çözüm oldu.unique_ptrboost::shared_ptrunique_ptrboost::shared_ptr

Ve gereksiz olduğu yerlerde kullanabilirsiniz. Yukarıda belirtildiği gibi, uygun kod yapısı bazı kullanımlara duyulan ihtiyacı ortadan kaldırabilir shared_ptr. Ancak sisteminiz bu şekilde yapılandırılamıyorsa ve hala ne gerekiyorsa yapıyorsa shared_ptr, önemli bir kullanım alanı olacaktır.


4
+1: Örneğin, artırma :: asio'ya bakın. Ben, sen UI widget veya asenkron çağrı bir nesneyi vazgeçmek son derleme zamanında bilmiyor olabilir düşüncesi birçok alanda kadar uzanır düşünüyorum ve shared_ptr ile size yok gerek bilmek. Açıkçası her durum için geçerli değil , alet kutusundaki bir başka (çok kullanışlı) araç.
Guy Sirton

3
Biraz geç yorum; shared_ptrc ++ 'nin python gibi bir betik dili ile entegre olduğu sistemler için mükemmeldir. Kullanımı boost::python, c ++ ve python tarafındaki referans sayımı büyük ölçüde işbirliği yapar; c ++ 'dan gelen herhangi bir nesne python içinde tutulabilir ve ölmez.
eudoxos

1
Sadece referans olarak, anlayışım ne WebKit ne de Chromium kullanımı değil shared_ptr. Her ikisi de kendi uygulamalarını kullanır intrusive_ptr. Onlar C ++ ile yazılmış büyük uygulamalar hem gerçek dünya örnekleri çünkü ben sadece bu konuyu
Gman

1
@gman: Yorumunuzu çok yanıltıcı buluyorum, çünkü Stroustrup, shared_ptreşit olarak geçerli olmak konusundaki itirazını intrusive_ptr: Konseptin herhangi bir yazımına değil, tüm ortak mülkiyet kavramına itiraz ediyor. Yani bu soruya amaçları için, bu büyük uygulamaların iki gerçek dünya örnekleridir do kullanın shared_ptr. (Ve dahası, shared_ptrbunun mümkün olmadığı durumlarda bile faydalı olduğunu gösteriyorlar weak_ptr.)
ruakh

1
FWIW, Bjarne'nin akademik dünyada yaşadığı iddiasına karşı koymak için: bütünüyle endüstriyel kariyerimde (bir G20 borsasının ortak mimarisini ve yalnızca 500.000 oyunculu bir MOG'un mimarisini dahil ederek) paylaşılan sahiplik. Burada Bjarne ile% 200'üm.
No-Bugs,

37

Hiç kullandığımma inanmıyorum std::shared_ptr.

Çoğu zaman, bir nesne, tüm ömrü boyunca ait olduğu bazı koleksiyonlarla ilişkilendirilir. Bu durumda , whatever_collection<o_type>ya da whatever_collection<std::unique_ptr<o_type>>, bu koleksiyonu bir nesnenin bir üyesi ya da bir otomatik değişken olarak kullanabilirsiniz. Elbette, dinamik sayıda nesneye ihtiyacınız yoksa, sadece otomatik sabit boyutlu bir diziyi kullanabilirsiniz.

Ne toplama boyunca yineleme, ne de nesne üzerindeki herhangi bir işlem, mülkiyeti paylaşmak için bir yardımcı işlev gerektirmez ... nesneyi kullanır, sonra döner ve arayan kişi, nesnenin tüm arama için canlı kalmasını garanti eder . Bu, arayan ve arayan arasında en çok kullanılan sözleşmedir.


Nicol Bolas, "Bazı nesneler çıplak bir işaretçiye tutulursa ve bu nesne ölürse ... hata!" Yorumunda bulundu. ve "Nesnelerin, nesnenin yaşamı boyunca yaşamasını sağlaması gerekir. Ancak shared_ptrbunu yapabilir."

Bu tartışmayı satın almıyorum. En azından shared_ptrbu sorunu çözmez. Ne dersin:

  • Eğer bir karma tablo bir nesneye tutulursa ve o nesnenin karma kodu değişirse, oops.
  • Bazı fonksiyonlar bir vektörü yineliyorsa ve bu vektöre bir eleman eklenirse ... oops.

Çöp toplama gibi, varsayılan kullanımı shared_ptr, programcıyı nesneler arasındaki veya işlev ile arayan arasındaki sözleşmeyi düşünmemeye teşvik eder. Doğru önkoşulları ve postkoşulları düşünmek gerekir ve nesne ömrü o büyük turtanın sadece küçük bir parçasıdır.

Nesneler "ölmez", bir kod parçası onları yok eder. Ve shared_ptrkontratı bulmak yerine soruna atmak sahte bir güvenliktir.


17
@ ronag: Ham işaretçinin daha iyi olacağı bir yerde kullanmaya başladığınızdan şüpheleniyorum çünkü "ham işaretçiler kötüdür". Ancak ham işaretçiler fena değil . Bir nesneye yalnızca ilk sahip olan işaretçiyi ham işaretçi yapmak kötüdür, çünkü o zaman istisnalar dışında önemsiz olan belleği el ile yönetmeniz gerekir. Ancak ham işaretçileri tutamaç veya yineleyici olarak kullanmak yeterlidir.
Ben Voigt

4
@BenVoigt: Elbette, çıplak işaretçilerin etrafından geçmenin zorluğu, nesnelerin ömrünü bilmediğinizdir. Bazı nesneler çıplak bir imleci tutarsa ​​ve o nesne ölürse ... oops. Bu tam olarak böyle bir şey shared_ptrve weak_ptrkaçınmak için tasarlandı. Bjarne, bir dünyada yaşamaya çalışır, her şeyin güzel, açık bir ömre sahip olduğu ve her şeyin onun etrafında inşa edildiği şeyler. Ve eğer o dünyayı kurabilirsen, harika. Ama gerçek dünyada böyle değil. Nesnelerin , nesnenin yaşamı boyunca yaşamasını sağlaması gerekir . shared_ptrBunu sadece yapabilir.
Nicol Bolas,

5
@ NicoBolas: Bu yanlış güvenlik. Bir işlevin arayanı normal garanti sağlamıyorsa: "Bu çağrı, işlev çağrısı sırasında herhangi bir dış tarafça dokunulmayacaktır", sonra her ikisinin de ne tür dış düzenlemelere izin verildiğine karar vermesi gerekir. shared_ptryalnızca belirli bir dış modifikasyonu hafifletir, en yaygın olanı bile değil. İşlev çağrısı sözleşmesi aksi belirtiyorsa, ömrünün doğru olduğundan emin olmak nesnenin sorumluluğunda değildir.
Ben Voigt

6
@NicolBolas: Eğer bir işlev bir nesne yaratır ve işaretçi ile döndürürse unique_ptr, nesneye sadece bir işaretçi bulunduğunu ve sahipliğini ifade eden bir ifade olmalıdır .
Ben Voigt

6
@Nicol: Bazı koleksiyonlarda bir işaretçi aranıyorsa, o koleksiyondaki işaretçi türü ne olursa olsun, ya da koleksiyon değerleri tutarsa ​​ham işaretçi kullanmanız gerekir. Bir nesne yaratıyorsa ve arayan bir a shared_ptristiyorsa, yine de a döndürmelidir unique_ptr. Dönüşüm unique_ptriçin shared_ptrkolay, ancak ters mantıksal imkansızdır.
Ben Voigt

16

Mutlak terimlerle düşünmeyi tercih ederim ("son çare" gibi) ama sorunlu alana göre.

C ++ kullanım ömrünü yönetmek için çeşitli yöntemler sunar. Bazıları nesneleri istifleme yöntemiyle yeniden üretmeye çalışıyor. Bazıları bu sınırlamadan kaçmaya çalışır. Bazıları "gerçek", bazıları ise yaklaşık değerlerdir.

Aslında şunları yapabilirsiniz:

  1. saf değer anlambilimi kullanır . İki varsayabiliriz önemli olan "değerler" değil, "kimlikler" dir nispeten küçük nesneler, için çalışır Personbir aynı olan nameaynı kişi (a aynı iki gösterimi daha iyi kişi ). Ömür boyu makine yığını tarafından verilir, programa end -essentially- deosn't madde (a beri kişi olduğunu o s adı ne olursa olsun Persontaşıyordur)
  2. yığın ayrılmış nesneleri ve ilgili referans veya işaretçileri kullanın: polimorfizme izin verir ve nesnenin ömrünü verir. “Akıllı işaretçilere” ihtiyaç duymazsınız, çünkü hiçbir nesnenin yığında işaret ettikleri nesneden daha uzun süre bırakılan yapılar tarafından "gösterilemeyeceğinden" emin olun (önce nesneyi oluşturun, sonra ona başvuran yapılar).
  3. stack yönetilen yığın tahsis edilmiş nesneleri kullanın : std :: vector ve tüm konteynerlerin yaptığı ve wat'ın std::unique_ptryaptığı şeydir (bunu 1 boyutunda bir vektör olarak düşünebilirsiniz). Yine, nesnenin, başvurdukları veri yapısından önce (sonra) var olmaya başladığını (ve varlıklarını sonlandırdığını) kabul ediyorsunuz.

Bu mehtodların zayıflığı, nesne türlerinin ve miktarlarının, yaratıldıkları yere göre daha derin yığın seviyesi çağrılarının yürütülmesi sırasında değişemeyeceğidir. Bütün bu teknikler, nesnenin yaratılması ve silinmesinin, kullanıcı faaliyetlerinin sonucu olduğu tüm durumlarda güçlerini "başarısız" eder, böylece nesnenin çalışma zamanı türü, derleme zamanı bilinen değildir ve nesnelere atıfta bulunan fazla yapılar olabilir. kullanıcı daha derin bir yığın seviyesi işlev çağrısından kaldırmak istiyor. Bu durumda, şunlardan birini yapmanız gerekir:

  • Nesne yönetimi ve ilgili sevk yapıları hakkında bazı disiplin tanıtmak ya da ...
  • bir şekilde "saf yığın temelli yaşamdan kaçma" nın karanlık tarafına git: nesne onları yaratan işlevlerden bağımsız olarak ayrılmalıdır. Ve ayrılmaları gerek ... ihtiyaç duyulana kadar .

C ++ isteslf bu olayı ( while(are_they_needed)) izlemek için herhangi bir yerel mekanizmaya sahip değildir , bu nedenle yaklaşık olarak:

  1. paylaşılan sahipliği kullan : nesneler yaşamı bir "referans sayacına" bağlıdır: "mülkiyet" hiyerarşik olarak düzenlenebilirse, mülkiyet döngülerinin mevcut olduğu yerlerde başarısız olur. Std :: shared_ptr'nin yaptığı budur. Ve weak_ptr döngüyü kırmak için kullanılabilir. Bu, çoğu zaman çalışır, ancak birçok tasarımcının farklı ekiplerde çalıştığı büyük tasarımlarda başarısız olur ve kimin kime sahip olduğu konusunda kesin bir neden (bir gereklilikten gelen bir şey) yoktur (tipik örnek çift beğeni zincirleridir): önceki sonraki atıfta bulunarak önceki veya sonraki önceki atıfta bulunarak önceki? atıfta bulunarak? Bir gereksinim yokluğunda, tho çözümleri eşdeğerdir ve büyük projede bunları karıştırmak için risk)
  2. Bir çöp toplama yığını kullanın : Basitçe ömür boyu umursamıyorsunuz. Kollektörü zaman zaman çalıştırıyorsunuz ve ulaşılamayan şeyin "artık gerekli değil" olduğu kabul ediliyor ve ... şey ... ahem ... yok edildi mi? kesinleşmiş? dondurulmuş?. Çok sayıda GC kollektörü var, ancak hiçbir zaman gerçekten C ++ konusunda farkında olan birini bulamam. Birçoğu boş hafıza, nesne imhasına aldırış etmiyor.
  3. C ++ uyumlu bir çöp toplayıcıyı uygun standart yöntemler arabirimiyle kullanın. Bulmak için iyi şanslar.

Sonuncusuna ilk çözüme gidildiğinde, nesnenin ömrünü yönetmek için gereken yardımcı veri yapısı miktarı, onu organize etmek ve sürdürmek için harcadığı zaman artar.

Çöp toplayıcının maliyeti var, paylaşılan_ptr daha az, unique_ptr daha az ve yığın yönetilen nesnelerin sayısı çok az.

shared_ptr"son çare" ?. Hayır, öyle değil: son çare çöp toplayıcıları. shared_ptrAslında std::önerilen son çaredir. Ama açıkladığım durumda iseniz, doğru çözüm olabilir.


9

Herb Sutter tarafından daha sonraki bir oturumda belirtilen tek şey, her kopyaladığınızda gerçekleşmesi gereken shared_ptr<>birbirine bağlı bir artış / azalış olmasıdır. Çok çekirdekli bir sistemde çoklu iş parçacıklı kodda, bellek eşitlemesi önemsiz değildir. Seçim göz önüne alındığında, ya bir yığın değeri, ya da a unique_ptr<>ve referansları veya ham işaretçileri kullanmak daha iyidir .


1
Veya shared_ptr
lvalue

8
Mesele şu shared_ptrki, sadece standartta olduğu için tüm hafıza sızıntısı problemlerinizi çözecek olan gümüş kurşun gibi kullanmayın . Baştan çıkarıcı bir tuzak, ancak kaynak sahipliğinin farkında olmak hala önemlidir ve bu mülkiyet paylaşılmadığı sürece shared_ptr<>, en iyi seçenek değildir.
Eclipse

Bana göre bu en az ayrıntı. Erken optimizasyona bakın. Çoğu durumda bu karar vermemelidir.
Guy Sirton,

1
@gbjbaanb: evet onlar cpu düzeyinde, ancak çok çekirdekli bir sistemde önbellekleri geçersiz kılıyor ve bellek engellerini zorluyorsunuz.
Eclipse

4
Üzerinde çalıştığım bir oyun projesinde performans farkının çok önemli olduğunu fark ettik; biri farklı olan 2 farklı tipte ref-sayıcı göstericiye ihtiyacımız vardı.
Kylotan

7

Son "çare" nin kullandığı kelimenin tam olup olmadığını hatırlamıyorum, ancak söylediklerinin gerçek anlamının son "seçim" olduğuna inanıyorum: net mülkiyet şartları; unique_ptr, weak_ptr, shared_ptr ve hatta çıplak işaretçiler kendi yerlerine sahiptir.

Hepsinin üzerinde anlaştıkları şeylerden biri, bizler (geliştiriciler, kitap yazarları vb.) C ++ 11'in “öğrenme aşaması” ndayız ve kalıplar ve stiller tanımlanıyor.

Örnek olarak, Herb , birkaç yıl süren Endüstri deneyimi ve en iyi uygulamaları ile Etkili C ++ (Meyers) ve C ++ Kodlama Standartları (Sutter & Alexandrescu) gibi bazı C ++ kitaplarının yeni sürümlerini beklememiz gerektiğini açıkladı . ++ 11 çıkıyor.


5

Sanırım ne elde ederse, herkesin standart bir işaretçi yazdığında (bir tür küresel değişim gibi) paylaşılan_ptr yazmalarının yaygınlaştığını ve aslında tasarlamak veya en azından bir çözümleme olarak kullanıldığını düşünüyorum. nesne oluşturma ve silme için planlama.

İnsanların unuttuğu bir diğer şey (yukarıdaki malzemede belirtilen kilitleme / güncelleme / kilit açma kilidinin yanı sıra), yalnızca paylaşılan_ptr'nin döngü sorunlarını çözmemesidir. Paylaşılan_ptr ile kaynak sızdırmaya devam edebilirsiniz:

Nesne A, başka bir Nesne için paylaşılan bir işaretçi içerir Nesne B, A a1 ve A a2 oluşturur ve a1.otherA = a2 atar; ve a2.otherA = a1; Şimdi, nesne B'nin a1, a2'yi oluşturmak için kullandığı ortak işaretçiler kapsam dışına çıkar (bir fonksiyonun sonunda söyler). Şimdi bir sızıntınız var - başka hiç kimse a1 ve a2'ye atıfta bulunmuyor, ancak birbirlerini ifade ediyorlar, böylece ref sayıları her zaman 1 olur ve siz sızdırdınız.

Bu basit bir örnek, bu gerçek kodda meydana geldiğinde, genellikle karmaşık şekillerde olur. Weak_ptr ile bir çözüm var, ancak pek çok kişi artık her yerde paylaşılan_ptr yapıyor ve sızıntı problemini ve hatta zayıf ptr'yi bile bilmiyor.

Sarmak için: OP'nin referans verdiği yorumların aşağıya kaydığını düşünüyorum:

Hangi dilde çalışıyor olursanız olun (yönetilen, yönetilmeyen veya paylaşılan_ptr gibi referans sayımları arasında olan bir şey) farketmeden, nesne oluşturmayı, yaşamları ve yıkımı anlamanız ve kasıtlı olarak karar vermeniz gerekir.

düzenleme: bu "bilinmeyen, bir paylaşılan_ptr kullanmalıyım" anlamına gelse bile, yine de düşündünüz ve bilerek yapıyorsunuz.


3

Tüm nesnelerin referansta sayıldığı ve öbeke tahsis edildiği bir dil olan Objective-C ile olan deneyimimden cevaplayacağım . Nesneleri tedavi etmenin bir yolu olduğundan, programlayıcı için işler çok daha kolaydır. Bu, kurallara uyulduğunda kod sağlamlığını garanti eden ve bellek sızıntısı sağlamayan standart kuralların tanımlanmasına olanak sağlamıştır. Ayrıca akıllı derleyici optimizasyonlarının son ARC (otomatik referans sayımı) gibi ortaya çıkmasını mümkün kılmıştır.

Demek istediğim şudur ki, shared_ptr son çare yerine ilk seçeneğiniz olmalıdır. Referans saymayı varsayılan olarak ve diğer seçeneklerle sadece ne yaptığınızdan eminseniz kullanın. Daha üretken olacaksınız ve kodunuz daha sağlam olacak.


1

Soruyu cevaplamaya çalışacağım:

Std :: shared_ptr olmadan nasıl program yapabiliriz ve nesne ömürlerini güvenli bir şekilde nasıl yönetebiliriz?

C ++ 'nın bellek yapmanın çok farklı yolları vardır, örneğin:

  1. struct A { MyStruct s1,s2; };Sınıf kapsamındaki paylaşılan_ptr yerine kullanın . Bu sadece gelişmiş programcılar içindir, çünkü bağımlılıkların nasıl çalıştığını anlamanızı gerektirir ve bağımlılıkları bir ağaçla sınırlandırmak için yeterince kontrol etme yeteneği gerektirir. Başlık dosyasındaki sınıfların sırası bunun önemli bir yönüdür. Bu kullanım hali hazırda yerleşik c ++ türlerinde yaygın olarak görülüyor, ancak programcı tanımlı sınıflarla kullanılması, bu bağımlılık ve sınıf sırası sorunlarından dolayı daha az kullanılmaya başlanmış görünüyor. Bu çözüm aynı zamanda sizeof ile ilgili sorunlar var. Programcılar bu konudaki sorunları ileriye dönük beyanlar veya gereksiz #includes kullanmak için bir gereklilik olarak görüyorlar ve bu nedenle birçok programcı daha düşük gösterici çözümüne ve daha sonra paylaşılan_ptr'e geri dönecek.
  2. Yerine MyClass &find_obj(int i);+ clone () kullanın shared_ptr<MyClass> create_obj(int i);. Birçok programcı yeni nesneler oluşturmak için fabrikalar oluşturmak istiyor. shared_ptr bu tür kullanımlar için idealdir. Sorun, basit yığın veya nesne tabanlı çözüm yerine, yığın / serbest mağaza tahsisi kullanarak karmaşık bellek yönetimi çözümü almasıdır. İyi C ++ sınıfı hiyerarşisi, yalnızca bir tanesini değil tüm bellek yönetimi düzenlerini destekler. Başvuru tabanlı çözüm, döndürülen nesne, yerel işlev kapsam değişkeni kullanmak yerine, içerik nesnesinin içinde saklanırsa çalışabilir. Mülkiyetin fabrikadan kullanıcı koduna geçirilmesinden kaçınılmalıdır. Find_obj () işlevini kullandıktan sonra nesneyi kopyalamak, onu ele almanın iyi bir yoludur - normal kopya kurucuları ve normal kurucu (farklı sınıftaki) refrerence parametresi veya polimorfik nesneler için klonlama () işleyebilir.
  3. İşaretçiler veya paylaşılan_ptr'ler yerine referansların kullanılması. Her c ++ sınıfının yapıcıları vardır ve her referans veri üyesinin başlatılması gerekir. Bu kullanım, işaretçilerin ve paylaşılan_ptr'lerin birçok kullanımından kaçınabilir. Sadece hafızanızın nesnenin içinde mi yoksa dışında mı olduğunu seçmeniz ve karara dayalı yapısal çözümü veya referans çözümü seçmeniz yeterlidir. Bu çözümle ilgili sorunlar genellikle yaygın olan ancak sorunlu pratik olan yapıcı parametrelerden kaçınmak ve sınıflar için arayüzlerin nasıl tasarlanması gerektiğini yanlış anlamakla ilgilidir.

"Mülkiyetin fabrikadan kullanıcı koduna geçirilmesinden kaçınılmalıdır." Ve bu mümkün olmadığında ne olur? "İşaretçiler veya paylaşılan_ptr'ler yerine referansların kullanılması." Um, hayır. İşaretçiler tekrar yerleştirilebilir. Referanslar olamaz. Bu, bir sınıfta depolananlar üzerinde inşaat zamanı kısıtlamalarını zorlar. Bu pek çok şey için pratik değil. Çözümünüz, daha akışkan bir arayüz ve kullanım modeli gereksinimlerine karşı çok katı ve esnek görünmemektedir.
Nicol Bolas,

@Nicol Bolas: Yukarıdaki kurallara uyduğunuzda, refs önerdiğiniz gibi veri depolamak için değil, nesneler arasındaki bağımlılıklar için kullanılacaktır. Bağımlılıklar verilerden daha kararlıdır, bu yüzden düşündüğünüz problemi asla çözemeyiz.
tp1

İşte çok basit bir örnek. Nesne olan bir oyun varlığınız var. Konuşması gereken bir hedef varlık olan başka bir nesneye başvurması gerekir. Ancak, hedefler değişebilir. Hedefler çeşitli noktalarda ölebilir . İşletmenin bu koşullarla başa çıkabilmesi gerekiyor. Kesintisiz işaretçi yaklaşımınız, hedef ölmek yerine, değişen hedefler kadar basit bir şeyi bile kaldıramaz.
Nicol Bolas,

nicol bolas: oh, farklı şekilde ele alınır; sınıfın arayüzü birden fazla "varlığı" destekler. Nesneler ve varlıklar arasında 1: 1 eşleme yerine, entityarray işlevini kullanırsınız. Sonra varlıklar, diziden çıkartarak kolayca ölürler. Oyunun tamamında sadece çok az sayıda işletme var ve diziler arasındaki bağımlılıklar çok sık değişmiyor :)
tp1

2
Hayır, unique_ptrfabrikalar için en uygunudur. Bir açabilirsiniz unique_ptrbir içine shared_ptr, ancak diğer yöne gitmek için mantıken imkansız.
Ben Voigt
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.