Akıllı işaretçiler: nesnenin sahibi kim? [kapalı]


114

C ++ tamamen bellek sahipliğiyle ilgilidir - diğer bir deyişle sahiplik semantiği .

Bu belleği serbest bırakmak, dinamik olarak ayrılmış bellek yığınının sahibinin sorumluluğundadır. Yani soru gerçekten hafızanın sahibi oluyor.

C ++ 'da sahiplik, ham işaretçi türüyle belgelenir, bu nedenle iyi bir (IMO) C ++ programında ham işaretçilerin dolaştığını görmek çok nadirdir ( nadirdir , hiçbir zaman değil ) (ham işaretçilerin çıkarılmış sahipliği olmadığı için hafızanın kime ait olduğunu söylemeyin ve bu nedenle belgeleri dikkatlice okumadan sahiplikten kimin sorumlu olduğunu söyleyemezsiniz).

Tersine, her ham işaretçinin kendi akıllı işaretçi sarmalayıcısında depolandığı bir sınıfta depolanan ham işaretçilerin görülmesi nadirdir. ( Not: Bir nesneye sahip değilseniz, onu depolamamalısınız çünkü ne zaman kapsam dışına çıkıp yok olacağını bilemezsiniz.)

O halde soru:

  • İnsanlar ne tür bir sahiplik anlamıyla karşılaşır?
  • Bu semantiği uygulamak için hangi standart sınıflar kullanılır?
  • Hangi durumlarda onları yararlı buluyorsunuz?

Cevap başına 1 tür anlamsal sahiplik bırakalım, böylece tek tek yukarı ve aşağı oylanabilirler.

Özet:

Kavramsal olarak, akıllı işaretçiler basittir ve saf bir uygulama kolaydır. Denenen pek çok uygulama gördüm, ancak her zaman bir şekilde, sıradan kullanım ve örnekler için açık olmayan bir şekilde kırıldılar. Bu nedenle, kendi kitaplığınızı döndürmek yerine her zaman bir kitaplıktaki iyi test edilmiş akıllı işaretçileri kullanmanızı öneririm. std::auto_ptrveya Boost akıllı işaretçilerinden biri tüm ihtiyaçlarımı karşılıyor gibi görünüyor.

std::auto_ptr<T>:

Tek kişi nesnenin sahibidir. Mülkiyet devrine izin verilir.

Kullanım: Bu, açıkça sahiplik aktarımını gösteren arayüzler tanımlamanıza olanak tanır.

boost::scoped_ptr<T>

Tek kişi nesnenin sahibidir. Mülkiyet devrine izin VERİLMEZ.

Kullanım: Açıkça sahiplik göstermek için kullanılır. Nesne, yıkıcı tarafından veya açıkça sıfırlandığında imha edilecektir.

boost::shared_ptr<T>( std::tr1::shared_ptr<T>)

Çoklu sahiplik. Bu basit bir referans sayılan göstericidir. Referans sayısı sıfıra ulaştığında nesne yok edilir.

Kullanım: Bir nesnenin, derleme zamanında belirlenemeyen bir ömre sahip birden fazla owers'ı olabileceği zaman.

boost::weak_ptr<T>:

shared_ptr<T>Bir işaretçiler döngüsünün meydana gelebileceği durumlarda kullanılır .

Kullanım: Yalnızca döngü paylaşılan bir yeniden sayımı sürdürürken, döngüleri nesneleri tutmadan durdurmak için kullanılır.


14
?? Soru neydi?
Pacerier

9
Bu soru gönderildiği için auto_ptr'nin (artık standartlaştırılmış) unique_ptr lehine kullanımdan kaldırıldığını belirtmek istedim
Juan Campa

In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good (IMO) Bu yeniden ifade edilebilir mi? Ben hiç anlamıyorum.
lolololol

@lolololol Cümleyi ikiye böldün. In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good C++ program it is very rare to see RAW pointers passed around. RAW işaretçilerinin sahiplik semantiği yoktur. Sahibini bilmiyorsanız, nesneyi silmekten kimin sorumlu olduğunu bilmiyorsunuz. Sahipliği tanımlayan işaretçileri sarmak için kullanılan birkaç standart sınıf vardır (std :: shared_ptr, std :: unique_ptr vb.) İşaretçinin silinmesinden kimin sorumlu olduğunu tanımlayın.
Martin York

1
C ++ 11 + 'da auto_ptr KULLANMAYIN! Bunun yerine unique_ptr kullanın!
val,

Yanıtlar:


20

Benim için bu 3 tür ihtiyacımın çoğunu karşılıyor:

shared_ptr - referans sayılan, sayaç sıfıra ulaştığında serbest bırakma

weak_ptr- yukarıdakinin aynısı, ancak bu bir ' shared_ptrköle'dir, ayrılamaz

auto_ptr- oluşturma ve serbest bırakma aynı işlev içinde gerçekleştiğinde veya nesnenin her zaman yalnızca tek sahip olarak kabul edilmesi gerektiğinde. Bir işaretçiyi diğerine atadığınızda, ikincisi nesneyi birinciden 'çalar'.

Bunlar için kendi uygulamam var, ancak içinde de mevcutlar Boost.

Nesneleri yine de referansla iletiyorum ( constmümkün olduğunda), bu durumda çağrılan yöntem nesnenin yalnızca arama sırasında canlı olduğunu varsaymalıdır.

Hub_ptr adını verdiğim, kullandığım başka bir tür işaretçi var . İçine yerleştirilmiş nesnelerden erişilebilir olması gereken bir nesneye sahip olduğunuz zamandır (genellikle sanal bir temel sınıf olarak). Bu, weak_ptronlara a göndererek çözülebilir , ancak shared_ptrkendi başına bir a'ya sahip değildir . Bu nesnelerin ondan daha uzun yaşamayacağını bildiği için onlara bir hub_ptr gönderir (bu sadece normal bir işaretçiye yönelik bir şablon sarmalayıcıdır).


2
Kendi işaretçi sınıfınızı (hub_ptr) oluşturmak yerine, neden bunu bu nesnelere iletip * referans olarak saklamasına izin vermiyorsunuz? Nesnelerin sahiplik sınıfıyla aynı anda yok edileceğini bile kabul ettiğiniz için, bu kadar çok çemberden atlamanın amacını anlamıyorum.
Michel

4
Temelde işleri netleştirmek için bir tasarım sözleşmesi. Alt nesne hub_ptr aldığında, sivri uçlu nesnenin çocuğun yaşamı boyunca yok edilmeyeceğini bilir ve ona sahip değildir. Hem içerilen hem de kapsayıcı nesneler açık bir kurallar kümesine katılır. Çıplak bir işaretçi kullanırsanız, kurallar belgelenebilir ancak derleyici ve kod tarafından uygulanmaz.
Fabio Ceconello

1
Ayrıca, hub_ptr'nin sürüm yapılarında açık bir işaretçiye yazılmasını sağlamak için #ifdefs'e sahip olabileceğinizi, böylece ek yükün yalnızca hata ayıklama yapısında var olacağını unutmayın.
Fabio Ceconello

3
Not Boost dokümantasyon scoped_ptr ilgili açıklamanızı çelişmektedir. Var olduğunu noncopyableve mülkiyetin devredilemeyeceğini belirtir.
Alec Thomas

3
@Alec Thomas, haklısın. Auto_ptr hakkında düşünüyordum ve scoped_ptr yazdım. Düzeltildi.
Fabio Ceconello

23

Basit C ++ Modeli

Gördüğüm çoğu modülde, varsayılan olarak, işaretçilerin sahipliğin almadığı varsayılıyordu . Aslında, bir işaretçinin sahipliğini terk eden işlevler / yöntemler hem çok nadirdi hem de bu gerçeği belgelerinde açıkça ifade ediyordu.

Bu model, kullanıcının yalnızca açıkça ayırdığı şeyin sahibi olduğunu varsayar . Geri kalan her şey otomatik olarak atılır (kapsam çıkışında veya RAII aracılığıyla). Bu, C benzeri bir modeldir ve çoğu işaretleyicinin, onları otomatik olarak veya ihtiyaç duyulduğunda (çoğunlukla söz konusu nesnelerin imhasında) serbest bırakacak nesnelere sahip olduğu ve nesnelerin yaşam süresinin tahmin edilebilir olduğu (RAII sizin arkadaşınızdır, tekrar).

Bu modelde, ham işaretçiler serbestçe dolaşmaktadır ve çoğunlukla tehlikeli değildir (ancak geliştirici yeterince akıllıysa, mümkün olduğunda bunun yerine referansları kullanacaktır).

  • ham işaretçiler
  • std :: auto_ptr
  • boost :: scoped_ptr

Akıllı Sivri C ++ Modeli

Akıllı işaretçilerle dolu bir kodda, kullanıcı nesnelerin ömrünü göz ardı etmeyi umabilir. Sahibi asla kullanıcı kodu değildir: Akıllı işaretçinin kendisidir (yine RAII). Sorun şu ki, referans sayılan akıllı işaretçilerle karıştırılan döngüsel referanslar ölümcül olabilir , bu nedenle hem paylaşılan işaretçilerle hem de zayıf işaretçilerle uğraşmanız gerekir. Yani, hala düşünmeniz gereken bir sahiplik var (zayıf işaretçi, ham işaretçiye göre avantajı size söyleyebilmesi olsa bile, hiçbir şeyi işaret edebilir).

  • boost :: shared_ptr
  • boost :: weak_ptr

Sonuç

Hayır ben tarif eden modeller önemli istisna olmadıkça, alıcı bir işaretçi olduğu değil sahipliğini alma ve kim kime ait olduğunu bilmek hala çok önemlidir . C ++ kodu için bile yoğun olarak referanslar ve / veya akıllı işaretçiler kullanan.


10

Sahiplik paylaşmayın. Bunu yaparsanız, yalnızca sizin kontrol etmediğiniz kodla olduğundan emin olun.

Sizi her şeyin nasıl etkileşime girdiğini anlamaya zorladığı için bu, sorunların% 100'ünü çözer.


2
  • Paylaşılan Sahiplik
  • boost :: shared_ptr

Bir kaynak birden çok nesne arasında paylaşıldığında. Boost shared_ptr, herkes finsih edildiğinde kaynağın ayrıldığından emin olmak için referans sayma kullanır.


2

std::tr1::shared_ptr<Blah> çoğu zaman en iyi bahsinizdir.


2
shared_ptr en yaygın olanıdır. Ama çok daha fazlası var. Her birinin kendi kullanım düzeni ve dava açılacak iyi ve kötü yerleri vardır. Biraz daha açıklama güzel olurdu.
Martin York

Daha eski bir derleyiciyle sıkışıp kaldıysanız, boost :: shared_ptr <blah> std :: tr1 :: shared_ptr <blah> 'nin dayandığı şeydir. Derleyiciniz Boost'un en son sürümü tarafından desteklenmiyor olsa bile, muhtemelen onu Boost'tan kopyalayabileceğiniz ve kullanabileceğiniz yeterince basit bir sınıftır.
Branan

2

Artırmadan, işaretçi konteyner kitaplığı da var. Nesneleri yalnızca kapsayıcıları bağlamında kullanacaksanız, bunlar standart bir akıllı işaretçi kabından biraz daha verimli ve kullanımı daha kolaydır.

Windows'ta, COM işaretçileri (IUnknown, IDispatch ve arkadaşlar) ve bunları işlemek için çeşitli akıllı işaretçiler vardır (örneğin, ATL'nin CComPtr'si ve _com_ptr sınıfına dayalı olarak Visual Studio'da "içe aktarma" ifadesi tarafından otomatik olarak oluşturulan akıllı işaretçiler ).


1
  • Bir sahip
  • boost :: scoped_ptr

Belleği dinamik olarak ayırmanız gerektiğinde, ancak bloğun her çıkış noktasında serbest bırakıldığından emin olmak istediğinizde.

Bunu, kolayca yeniden yerleştirilebildiği ve bir sızıntı konusunda endişelenmek zorunda kalmadan serbest bırakılabildiği için yararlı buluyorum.


1

Tasarımımda hiçbir zaman ortak mülkiyete sahip olduğumu sanmıyorum. Aslında, kafamın tepesinden düşünebildiğim tek geçerli durum Flyweight modelidir.


1

yasper :: ptr hafif, boost :: shared_ptr benzeri bir alternatiftir. Küçük projemde (şimdilik) iyi çalışıyor.

Http://yasper.sourceforge.net/ adresindeki web sayfasında aşağıdaki şekilde açıklanmaktadır:

Neden başka bir C ++ akıllı işaretçi yazasınız? C ++ için halihazırda pek çok yüksek kaliteli akıllı işaretçi uygulaması vardır, en belirgin olarak Boost işaretçi panteonu ve Loki'nin SmartPtr'i. Akıllı işaretçi uygulamalarının iyi bir karşılaştırması için ve kullanımları uygun olduğunda lütfen Herb Sutter'ın Yeni C ++: Akıllı (er) İşaretçilerini okuyun. Diğer kütüphanelerin kapsamlı özelliklerinin aksine Yasper, dar odaklı bir referans sayma işaretçisidir. Boost'un shared_ptr ve Loki'nin RefCounted / AllowConversion politikalarıyla yakından ilgilidir. Yasper, C ++ programcılarının Boost'un büyük bağımlılıklarını tanıtmadan veya Loki'nin karmaşık ilke şablonlarını öğrenmeye gerek kalmadan bellek yönetimini unutmasına izin veriyor. Felsefe

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)

Yasper, diğer uygulamalar tarafından izin verilmeyen riskli (ancak yararlı) eylemlere (ham işaretleyicilere atama ve manuel yayınlama gibi) izin verdiği için son nokta tehlikeli olabilir. Dikkatli olun, bu özellikleri yalnızca ne yaptığınızı biliyorsanız kullanın!


1

Tek-devredilebilir-sahip'in sık kullanılan başka bir biçimi daha vardır ve atama anlamındaki çılgın yolsuzluğun auto_ptrneden olduğu sorunları ortadan kaldırdığı için tercih edilir auto_ptr.

Başka hiçbir şeyden bahsetmiyorum swap. Uygun bir swapişleve sahip herhangi bir tür, sahiplik aynı türden başka bir örneğe değiştirilerek aktarılıncaya kadar sahip olduğu bazı içeriğe akıllı bir referans olarak düşünülebilir . Her örnek kimliğini korur ancak yeni içeriğe bağlanır. Güvenli bir şekilde yeniden doğrulanabilir bir referans gibi.

(Bu, akıllı bir işaretçiden ziyade akıllı bir referanstır çünkü içeriğe ulaşmak için ondan açıkça başvurmak zorunda değilsiniz.)

Bu, auto_ptr'nin daha az gerekli olduğu anlamına gelir - yalnızca türlerin iyi bir swapişleve sahip olmadığı boşlukları doldurmak için gereklidir . Ancak tüm standart kaplar yapar.


Belki daha az gerekli hale gelir (scoped_ptr'nin bunu bundan daha az gerekli kıldığını söyleyebilirim), ama ortadan kalkmıyor. Bir takas işlevine sahip olmak, yığın üzerinde bir şey ayırırsanız ve siz onu silmeden önce birisi atarsa ​​veya siz unutursanız, size hiç yardımcı olmaz.
Michel

Son paragrafta da aynen böyle söyledim.
Daniel Earwicker

0
  • Bir Sahip: Aka Kopyada sil
  • std :: auto_ptr

Nesnenin yaratıcısı, mülkiyeti açıkça başka birine vermek istediğinde. Bu aynı zamanda size bunu verdiğim kodu belgelemenin bir yoludur ve artık onu takip etmiyorum, bu yüzden bitirdiğinizde sildiğinizden emin olun.

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.