akıllı işaretçiler (boost) açıkladı


220

Aşağıdaki işaretçi kümesi arasındaki fark nedir? Üretim kodunda her bir işaretçiyi ne zaman kullanırsınız?

Örnekler takdir edilecektir!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

Üretim kodunda boost kullanıyor musunuz?

Yanıtlar:


339

Akıllı işaretçilerin temel özellikleri

Her akıllı işaretçiyi atayabileceğiniz özelliklere sahip olduğunuzda kolaydır. Üç önemli özellik vardır.

  • hiç sahiplik yok
  • mülkiyet devri
  • mülkiyet payı

Birincisi, akıllı bir işaretçinin nesneye sahip olmadığı için nesneyi silemeyeceği anlamına gelir. İkincisi, aynı anda yalnızca bir akıllı işaretçinin aynı nesneyi gösterebileceği anlamına gelir. Akıllı işaretçi işlevlerden döndürülecekse, örneğin sahiplik iade edilen akıllı işaretleyiciye aktarılır.

Üçüncüsü, çoklu akıllı işaretçilerin aynı nesneyi aynı anda gösterebileceği anlamına gelir. Bu uygulanır ham pointer ancak ham işaretçileri önemli özellik olmadığından, çok: Onlar ister tanımlamaz sahibi ya da değil. Her sahip nesneyi bıraktığında, sahiplik akıllı işaretçisinin paylaşımı nesneyi siler. Bu davranışa sık sık ihtiyaç duyulur, bu nedenle akıllı işaretçilerin sahibi olmak yaygın olarak yayılır.

Bazı akıllı işaretçiler ne ikincisini ne de üçüncüsünü desteklemez. Bu nedenle işlevlerden döndürülemez veya başka bir yere geçirilemezler. Hangisi için en uygunRAIIAkıllı işaretçinin yerel tutulduğu amaçlar ve sadece bir nesneyi kapsam dışına çıktıktan sonra serbest bırakır.

Mülkiyet payı bir kopya kurucuya sahip olarak uygulanabilir. Bu, doğal olarak akıllı bir işaretçiyi kopyalar ve hem kopya hem de orijinal aynı nesneyi referans alır. Sahiplik aktarımı şu anda C ++ 'da gerçekten uygulanamıyor çünkü bir şeyi dil tarafından desteklenen bir nesneden diğerine aktarmanın bir yolu yok: Bir nesneyi bir işlevden döndürmeye çalışırsanız, olan şey nesnenin kopyalanmasıdır. Dolayısıyla, sahiplik aktarımını uygulayan akıllı bir işaretçi, sahiplik aktarımını uygulamak için kopya yapıcısını kullanmalıdır. Bununla birlikte, bu da kaplarda kullanımını bozar, çünkü gereksinimler kapların elemanlarının kopya yapıcısının bu akıllı işaretçilerin bu "hareketli kurucu" davranışı ile uyumlu olmayan belirli bir davranışını belirtir.

C ++ 1x, "move yapıcıları" ve "move atama işleçleri" tanıtarak sahiplik aktarımı için yerel destek sağlar. Ayrıca böyle bir mülkiyet devri akıllı işaretçisi ile birlikte gelir unique_ptr.

Akıllı işaretçileri kategorilere ayırma

scoped_ptrne aktarılabilir ne de paylaşılabilir akıllı bir göstergedir. Yerel olarak bellek ayırmanız gerekiyorsa kullanılabilir, ancak kapsam dışı olduğunda tekrar serbest bırakıldığından emin olun. Ama yine de, isterseniz başka bir scoped_ptr ile değiştirilebilir.

shared_ptrsahipliğini paylaşan akıllı bir işaretçi (yukarıdaki üçüncü tür). Referans sayılır, böylece son kopyasının kapsam dışına çıktığında ve yönetilen nesneyi serbest bıraktığında görebilir.

weak_ptrsahibi olmayan bir akıllı işaretçi. Referans sayısı eklemeden yönetilen bir nesneye (paylaşılan_ptr tarafından yönetilen) referans vermek için kullanılır. Normalde, ham işaretçiyi shared_ptr öğesinden çıkarmanız ve kopyalamanız gerekir. Ancak nesnenin gerçekten ne zaman silindiğini kontrol etmenin bir yolu olmadığından bu güvenli olmaz. Bu nedenle, zayıf_ptr, paylaşılan_ptr tarafından yönetilen bir nesneye başvurarak araç sağlar. Nesneye erişmeniz gerekiyorsa, nesnenin yönetimini kilitleyebilirsiniz (başka bir iş parçacığında, nesneyi kullanırken bir shared_ptr öğesinin onu serbest bırakmasını önlemek için) ve ardından onu kullanabilirsiniz. Poor_ptr zaten silinmiş bir nesneyi gösteriyorsa, bir istisna atarak sizi fark edecektir. Döngüsel bir referansınız olduğunda poor_ptr kullanmak en yararlısıdır: Referans sayımı böyle bir durumla kolayca baş edemez.

intrusive_ptrbir paylaşılan_ptr gibidir ancak referans sayısını paylaşılan_ptr içinde tutmaz, ancak sayımı yönetilen nesne tarafından tanımlanması gereken bazı yardımcı işlevlere arttırır / azaltır. Bunun avantajı, daha önce referans alınan bir nesnenin (harici bir referans sayma mekanizması tarafından artırılan bir referans sayısı olan) bir intrusive_ptr içine doldurulabilmesidir - çünkü referans sayısı artık akıllı işaretçinin içinde değildir, ancak akıllı işaretçi mevcut bir referans sayma mekanizması.

unique_ptrsahiplik göstergesinin aktarılmasıdır. Kopyalayamazsınız, ancak C ++ 1x'in hareket yapıcılarını kullanarak taşıyabilirsiniz:

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

Bu, std :: auto_ptr'in uyduğu semantiktir, ancak taşınması için yerel desteğin eksik olması nedeniyle, onlara tuzaklar olmadan sağlayamaz. unique_ptr, taşıma semantiğinin temel özelliklerinden biri olan geçici bir unique_ptr kaynağından otomatik olarak kaynakları çalacaktır. auto_ptr, sonraki C ++ Standard sürümünde unique_ptr lehine kullanımdan kaldırılacaktır. C ++ 1x, yalnızca hareket edebilen ancak kaplara kopyalanamayan nesnelerin doldurulmasına da izin verir. Böylece unique_ptr'leri bir vektöre aktarabilirsiniz. Burada duracağım ve bunun hakkında daha fazla okumak isterseniz sizi bu konuyla ilgili güzel bir makaleye yönlendireceğim .


3
övgü için teşekkürler dostum. i minnettarım, bu yüzden şimdi de +1 alacaksın: p
Johannes Schaub - litb

@litb: "Mülkiyet devri" konusunda bir kuşkum var; C ++ 03'te nesneler arasında gerçek bir mülkiyet devri olmadığını kabul ediyorum , ancak akıllı işaretçiler için bu, burada belirtilen yıkıcı kopya mekanizmasıyla yapılamaz informit.com/articles/article.aspx?p=31529&seqNum= 5 .
legends2k

3
harika cevap. Not: auto_ptrzaten kullanımdan kaldırıldı (C ++ 11).
nickolay

2
"bu da kaplarda kullanımını bozar, çünkü gereksinimler kapların öğelerinin kopya yapıcısının bu akıllı işaretçilerin bu" hareketli kurucu "davranışı ile bağdaşmayan belirli bir davranışını belirtir." O kısmı alamadım.
Raja

Daha iyi önbellek tutarlılığı için intrusive_ptrtercih edilebileceği de söylendi shared_ptr. Görünüşe göre, referans sayısını ayrı bir nesne yerine yönetilen nesnenin kendisinin belleğinin bir parçası olarak saklarsanız daha iyi performans gösterir. Bu, yönetilen nesnenin bir şablonunda veya üst sınıfında uygulanabilir.
Eliot

91

scoped_ptr en basitidir. Kapsam dışına çıktığında yok edilir. Aşağıdaki kod geçersizdir (scoped_ptrs kopyalanamaz) ancak bir noktayı gösterir:

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

shared_ptr referans sayılır. Her kopya veya atama gerçekleştiğinde, referans sayımı artırılır. Bir örneğin yıkıcısı her tetiklendiğinde, ham T * için referans sayısı azaltılır. 0 olduğunda, işaretçi serbest bırakılır.

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

poor_ptr , shared_ptr'e yönlendirilen işaretin hala etrafta olup olmadığını kontrol etmenizi gerektiren paylaşılan bir işaretçiye zayıf başvuru

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

intrusive_ptr genellikle kullanmanız gereken bir 3. taraf akıllı ptr olduğunda kullanılır. Referans sayısını eklemek ve azaltmak için ücretsiz bir işlev çağırır.Daha fazla bilgi için belgeleri artırma bağlantısına bakın .


isnt if (tPtrAccessed[0].get() == 0)varsayalım if (tPtrAccessed.get() == 0) ?
Rajeshwar

@DougT. Java'nın Referanslar ile aynı fikri kullandığına inanıyor musunuz? Yumuşak, Sert, Zayıf vb?
gansub

20

boost::ptr_containerHerhangi bir boost akıllı işaretçi araştırmasında göz ardı etmeyin . Örneğin std::vector<boost::shared_ptr<T> >, çok yavaş olacağı durumlarda çok değerli olabilirler .


Aslında, en son denediğimde, kıyaslama en azından tipik PC HW'de yazdığımdan beri performans farkının önemli ölçüde kapandığını gösterdi! Daha verimli ptr_container yaklaşımı yine de niş kullanım durumlarında bazı avantajlara sahip olabilir.
timday

12

İkinci olarak belgelere bakmayla ilgili tavsiyem. Göründüğü kadar korkutucu değil. Ve birkaç kısa ipucu:

  • scoped_ptr- kapsam dışına çıktığında bir işaretçi otomatik olarak silinir. Not - atama yapılamaz, ancak ek yük getirmez
  • intrusive_ptr- ek yükü olmayan referans sayma işaretçisi smart_ptr. Ancak nesnenin kendisi referans sayısını saklar
  • weak_ptr- shared_ptrDairesel bağımlılıklarla sonuçlanan durumlarla başa çıkmak için birlikte çalışır (belgeleri okuyun ve Google'da güzel resim arayın;)
  • shared_ptr - akıllı göstergelerin genel, en güçlü (ve ağır ağırlığı) (boost tarafından sunulanlardan)
  • Ayrıca, auto_ptrkontrol bir kapsamdan çıktığında işaret ettiği nesnenin otomatik olarak yok edilmesini sağlayan eski vardır . Ancak diğerlerinden farklı kopya semantiği vardır.
  • unique_ptr- C ++ 0x ile gelecek

Düzenleme yanıtı: Evet


8
Buraya geldim çünkü destek belgelerini çok korkutucu buldum.
Francois Botha
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.