Hangi C ++ Akıllı İşaretçi Uygulamaları mevcuttur?


121

Karşılaştırmalar, Artıları, Eksileri ve Ne Zaman Kullanılmalı?

Bu, basit bir cevap olduğunu düşündüğüm şeyin bazı özel akıllı işaretçi uygulamaları hakkında birçok yorum ürettiği bir çöp toplama dizisinden bir yan üründür, bu nedenle yeni bir gönderiye başlamaya değer görünüyordu.

Nihayetinde soru, C ++ 'da akıllı işaretçilerin çeşitli uygulamaları nelerdir ve nasıl karşılaştırılır? Sadece basit artılar ve eksiler veya istisnalar ve aksi halde işe yarayacağını düşündüğünüz bir şeye ilişkin sorunlar.

Kullandığım veya en azından gözden kaçırdığım ve cevap olarak kullanmayı düşündüğüm bazı uygulamaları ve% 100 doğru olmayabilecek farklılıklarını ve benzerliklerini anladığımı bildirdim, bu yüzden gerektiğinde beni doğrulamaktan veya düzeltmekten çekinmeyin.

Amaç, bazı yeni nesneler ve kitaplıklar hakkında bilgi edinmek veya halihazırda yaygın olarak kullanılan mevcut uygulamaları kullanımımı ve anlayışımı düzeltmek ve diğerleri için uygun bir referansla sonuçlanmaktır.


5
Bence bu, bu soruya bir cevap olarak yeniden gönderilmeli ve soru gerçek bir soru haline getirilmelidir. Aksi takdirde, insanların bunu "gerçek bir soru değil" olarak kapatacağını hissediyorum.
strager

3
ATL akıllı işaretçileri veya OpenSceneGraph'lar gibiosg::ref_ptr diğer birçok akıllı işaretçi vardır .
James McNellis

11
Burada bir soru var mı?
Cody Grey

6
Sanırım yanlış anladınız std::auto_ptr. std::auto_ptr_refbir tasarım detayıdır std::auto_ptr. std::auto_ptrçöp toplama ile ilgisi yoktur, asıl amacı, özellikle fonksiyon çağrısı ve fonksiyon dönüş durumlarında istisnai güvenli sahiplik aktarımına izin vermektir. std::unique_ptryalnızca standart kapsayıcılarla alıntı yaptığınız "sorunları" çözebilir çünkü C ++, taşıma ve kopyalama arasında bir ayrım yapılmasına izin verecek şekilde değişmiştir ve standart kapsayıcılar bundan yararlanmak için değiştirilmiştir.
CB Bailey

3
Akıllı işaretçiler konusunda uzman olmadığınızı söylüyorsunuz, ancak özetinizin oldukça kapsamlı ve doğru olduğunu söylüyorsunuz ( auto_ptr_refbir uygulama detayı olmakla ilgili küçük tartışmalar dışında ). Yine de, bunu bir cevap olarak göndermeniz ve soruyu gerçek bir soru olacak şekilde yeniden formüle etmeniz gerektiğini kabul ediyorum . Bu, daha sonra gelecekteki bir referans olarak hizmet edebilir.
Konrad Rudolph

Yanıtlar:


231

C ++ 03

std::auto_ptr- Belki de ilk taslak sendromundan muzdarip olan orijinallerden biri yalnızca sınırlı çöp toplama olanağı sağlıyor. İlk dezavantajı, deletediziye tahsis edilmiş nesneleri ( new[]) tutmak için onları yok etmeye çağırmasıdır . İşaretçinin sahipliğini alır, böylece iki otomatik işaretçi aynı nesneyi içermemelidir. Atama, sahipliği aktaracak ve rvalue otomatik işaretçisini bir boş göstericiye sıfırlayacaktır . Bu belki de en kötü dezavantaja yol açar; Yukarıda belirtilen kopyalanamama nedeniyle STL kaplarında kullanılamazlar. Herhangi bir kullanım senaryosuna son darbe, bir sonraki C ++ standardında kullanımdan kaldırılmalarıdır.

std::auto_ptr_ref- Bu akıllı bir işaretçi değildir, aslında std::auto_ptrbelirli durumlarda kopyalama ve atamaya izin vermek için birlikte kullanılan bir tasarım detayıdır . Özellikle, mülkiyeti devretmek için bir hareket oluşturucu olarak da bilinen Colvin-Gibbons hilesini kullanarak const olmayan std::auto_ptrbir değere dönüştürmek için kullanılabilir .

Aksine, belki de std::auto_ptrotomatik çöp toplama için genel amaçlı bir akıllı işaretçi olarak kullanılmak üzere tasarlanmamıştı. Sınırlı anlayışımın ve varsayımlarımın çoğu, Herb Sutter'ın auto_ptr'nin Etkili Kullanımına dayanmaktadır ve her zaman en optimize şekilde olmasa da düzenli olarak kullanıyorum.


C ++ 11

std::unique_ptr- Dizilerle çalışma, özel kopya oluşturucu aracılığıyla ldeğer koruması, STL kapsayıcıları ve algoritmaları ile kullanılabilir olma gibi std::auto_ptrzayıflıkları düzeltmek için temel iyileştirmeler dışında, onu değiştirecek olan arkadaşımız oldukça benzer olacaktır . Performans ek yükü olduğundan ve bellek ayak izi sınırlıdır bu, ham işaretçileri değiştirmek için ideal bir adaydır veya belki de daha uygun bir şekilde sahip olmak olarak tanımlanır. "Benzersiz" ifadesinin ima ettiği gibi, önceki gibi işaretçinin yalnızca bir sahibi vardır .std::auto_ptrstd::auto_ptr

std::shared_ptr- Bunun TR1'e dayandığına ve boost::shared_ptrtakma ad ve işaretçi aritmetiğini de içerecek şekilde geliştirildiğine inanıyorum . Kısacası, dinamik olarak tahsis edilmiş bir nesnenin etrafına referans sayılan akıllı işaretçiyi sarar. "Paylaşılan" ifadesi, son paylaşılan işaretçinin son referansı kapsam dışına çıktığında işaretçinin birden fazla paylaşılan işaretçiye ait olabileceğini ima ettiğinden, nesne uygun şekilde silinecektir. Bunlar ayrıca iş parçacığı açısından güvenlidir ve çoğu durumda eksik türleri işleyebilir. varsayılan ayırıcı kullanılarak tek bir yığın tahsisi ile std::make_sharedverimli bir şekilde oluşturmak için kullanılabilir std::shared_ptr.

std::weak_ptr- Aynı şekilde TR1 ve boost::weak_ptr. Bu, a'nın sahip olduğu bir nesneye bir referanstır std::shared_ptrve bu nedenle std::shared_ptrreferans sayısı sıfıra düşerse nesnenin silinmesini engellemeyecektir . Ham işaretçiye erişmek için önce , sahip olunan işaretçinin süresi dolmuşsa ve zaten yok edilmişse boş bir döndüren std::shared_ptrby çağrıya erişmeniz gerekir . Bu, birden çok akıllı işaretçi kullanırken belirsiz asılı referans sayılarından kaçınmak için öncelikle kullanışlıdır.lockstd::shared_ptr


artırmak

boost::shared_ptr- Muhtemelen en çeşitli senaryolarda (STL, PIMPL, RAII, vb.) Kullanımı en kolay olan bu, paylaşılan referanslı sayılan akıllı işaretçidir. Bazı durumlarda performans ve ek yük hakkında birkaç şikayet duydum, ancak bunları görmezden gelmiş olmalıyım çünkü tartışmanın ne olduğunu hatırlayamıyorum. Görünüşe göre, beklemede olan bir standart C ++ nesnesi olacak kadar popülerdi ve akıllı işaretçilerle ilgili norm üzerinde hiçbir dezavantaj akla gelmiyor.

boost::weak_ptr- std::weak_ptrBu uygulamaya dayalı olarak, önceki açıklamaya benzer şekilde , bu, bir boost::shared_ptr. Şaşırtıcı bir şekilde lock()"güçlü" paylaşılan işaretçiye erişmek için çağrı yapmazsınız ve zaten yok edilmiş olabileceği için geçerli olduğundan emin olmak için kontrol etmeniz gerekir. Döndürülen paylaşılan işaretçiyi saklamadığınızdan emin olun ve işiniz biter bitmez kapsam dışına çıkmasına izin verin, aksi takdirde referans sayılarınızın takılacağı ve nesnelerin yok edilmeyeceği döngüsel referans problemine geri dönersiniz.

boost::scoped_ptr- Bu, muhtemelen boost::shared_ptrkullanılabildiğinde daha iyi performans gösteren bir alternatif için tasarlanmış, az ek yükü olan basit bir akıllı işaretçi sınıfıdır . std::auto_ptrÖzellikle bir STL kabının bir öğesi olarak veya aynı nesneye birden çok işaretçi ile güvenli bir şekilde kullanılamaması gerçeğiyle karşılaştırılabilir .

boost::intrusive_ptr- Bunu hiç kullanmadım ama benim anlayışıma göre, kendi akıllı işaretçi uyumlu sınıflarınızı oluştururken kullanılmak üzere tasarlandı. Referans sayımını kendiniz uygulamanız gerekir, ayrıca sınıfınızın genel olmasını istiyorsanız birkaç yöntemi uygulamanız gerekir, ayrıca kendi iş parçacığı güvenliğinizi uygulamanız gerekir. Artı tarafta, bu muhtemelen size tam olarak ne kadar veya ne kadar "akıllılık" istediğinizi seçmenin ve seçmenin en özel yolunu verir. intrusive_ptrgenellikle shared_ptrnesne başına tek bir yığın tahsisine sahip olmanıza izin verdiğinden daha verimlidir . (teşekkürler Arvid)

boost::shared_array- Bu boost::shared_ptrdiziler için. Temel olarak new [], operator[]ve tabii ki delete []içinde pişirilir. Bu STL kaplarında kullanılabilir ve bildiğim kadarıyla her şeyi boost:shared_ptryapar, ancak bunlarla kullanamasanız boost::weak_ptrda. Bununla birlikte, alternatif boost::shared_ptr<std::vector<>>olarak benzer işlevler için ve boost::weak_ptrreferanslar için kullanma yeteneğini yeniden kazanmak için bir kullanabilirsiniz .

boost::scoped_array- Bu boost::scoped_ptrdiziler için. Gibi boost::shared_arraytüm gerekli dizi iyilik pişirilir. Bu olmayan bir copyable ve böylece STL kaplarda kullanılamaz. Neredeyse her yerde kendinizi bunu kullanmak isterken buldum, muhtemelen kullanabileceksiniz std::vector. Hangisinin gerçekte daha hızlı veya daha az ek yüke sahip olduğunu asla belirlemedim, ancak bu kapsamlı dizi bir STL vektöründen çok daha az ilgili görünüyor. Yığın üzerinde tahsis tutmak istediğinizde boost::arraybunun yerine düşünün .


Qt

QPointer- Qt 4.0'da tanıtılan bu, yalnızca QObjectsınıflarla çalışan ve türetilen sınıflarla çalışan "zayıf" bir akıllı göstericidir , Qt çerçevesinde neredeyse her şeydir, bu yüzden bu gerçekten bir sınırlama değildir. Bununla birlikte, "güçlü" bir işaretçi sağlamaması gibi sınırlamalar vardır ve temeldeki nesnenin geçerli olup olmadığını kontrol edebilmenize rağmen, isNull()özellikle çok iş parçacıklı ortamlarda bu denetimi geçtikten hemen sonra nesnenizin yok edildiğini görebilirsiniz. Qt insanlar inanıyorum ki bunun kullanımdan kaldırıldığını düşünüyor.

QSharedDataPointer- Bu, boost::intrusive_ptrbazı yerleşik iş parçacığı güvenliğine sahip olmasına rağmen, potansiyel olarak karşılaştırılabilir "güçlü" bir akıllı işaretçidir, ancak alt sınıflandırma yoluyla yapabileceğiniz referans sayma yöntemlerini ( refve deref) eklemenizi gerektirir QSharedData. Qt'nin çoğunda olduğu gibi, nesneler en iyi şekilde geniş kalıtım yoluyla kullanılır ve her şeyin alt sınıflara ayrılması amaçlanan tasarım gibi görünür.

QExplicitlySharedDataPointer- QSharedDataPointerÖrtük olarak çağırmaması dışında çok benzer detach(). Bu sürüm 2.0'ı QSharedDataPointer, referans sayısı sıfıra düştükten sonra tam olarak ne zaman ayrılacağına dair kontroldeki hafif artış, özellikle yepyeni bir nesneye değmez diye adlandırıyorum.

QSharedPointer- Atomik referans sayma, iş parçacığı güvenli, paylaşılabilir işaretçi, özel silmeler (dizi desteği), akıllı bir işaretçinin olması gereken her şey gibi sesler. Bu, Qt'de öncelikle akıllı bir işaretçi olarak kullandığım şeydir ve bunu boost:shared_ptr, Qt'deki birçok nesne gibi muhtemelen önemli ölçüde daha fazla ek yük ile karşılaştırılabilir buluyorum .

QWeakPointer- Tekrarlayan bir model hissediyor musunuz? Tıpkı std::weak_ptrve boost::weak_ptrbu, QSharedPointeriki akıllı işaretçi arasında, aksi takdirde nesnelerinizin asla silinmemesine neden olacak referanslara ihtiyaç duyduğunuzda kullanılır .

QScopedPointer- Bu ad da tanıdık gelmeli ve aslında boost::scoped_ptrpaylaşılan ve zayıf işaretçilerin Qt sürümlerinden farklı olarak temel almalıdır . Ek yükü olmadan tek bir sahipli akıllı işaretçi sağlama işlevi görür ve QSharedPointerbu da onu uyumluluk, istisnai güvenli kod ve kullanabileceğiniz std::auto_ptrveya kullanabileceğiniz her şey için daha uygun hale getirir boost::scoped_ptr.


1
Bahsetmeye değer olduğunu düşündüğüm iki şey: nesne başına tek bir yığın tahsisine sahip olmanıza izin verdiği intrusive_ptriçin genellikle daha verimlidir shared_ptr. shared_ptrgenel durumda referans sayaçları için ayrı bir küçük yığın nesnesi tahsis edecektir. std::make_sharedher iki dünyanın da en iyisini elde etmek için kullanılabilir. shared_ptrsadece tek bir yığın tahsisi ile.
Arvid

Belki de ilgisiz bir sorum var: Çöp toplama, tüm işaretçileri shared_ptrs ile değiştirerek uygulanabilir mi? (Döngüsel referansları çözme sayılmaz)
Seth Carnegie

@Seth Carnegie: Tüm işaretçiler ücretsiz mağazada tahsis edilen bir şeye işaret etmeyecek.
In silico

2
@the_mandrill Ancak, sahip sınıfın yıkıcısı, Pimpl deyiminde yine de verilen istemci kodundan ayrı bir çeviri biriminde (.cpp-dosyası) tanımlanırsa çalışır. Çünkü bu çeviri birimi genellikle Pimpl'in tam tanımını bilir ve dolayısıyla yıkıcısı (auto_ptr'yi yok ettiğinde) Pimpl'i doğru şekilde yok eder. Bu uyarıları gördüğümde bununla ilgili korkularım da vardı, ama denedim ve işe yarıyor (Pimpl'in yıkıcısı çağrılıyor). Not: Herhangi bir yanıt görmek için lütfen @-sözdizimini kullanın.
Christian Rau

2
Dokümanlara uygun bağlantılar eklenerek listenin kullanışlılığı artırıldı.
ulidtko


1

Verilenlere ek olarak, bazı güvenlik odaklı olanlar da var:

SaferCPlusPlus

mse::TRefCountingPointergibi akıllı işaretçiyi sayan bir referanstır std::shared_ptr. Aradaki fark mse::TRefCountingPointer, daha güvenli, daha küçük ve daha hızlı olması, ancak herhangi bir iplik emniyet mekanizmasına sahip olmamasıdır. Ve her zaman geçerli olarak tahsis edilmiş bir nesneyi işaret ettiği güvenli bir şekilde varsayılabilen "boş olmayan" ve "sabit" (yeniden hedeflenemez) sürümlerde gelir. Temel olarak, hedef nesneniz eşzamansız iş parçacıkları arasında paylaşılıyorsa std::shared_ptr, o zaman kullanın , aksi takdirde mse::TRefCountingPointerdaha uygun olur.

mse::TScopeOwnerPointerbenzer boost::scoped_ptr, ancak birlikte çalışır mse::TScopeFixedPointergibi bir "güçlü-zayıf" işaretçi ilişkisi ayni std::shared_ptrve std::weak_ptr.

mse::TScopeFixedPointer, yığın üzerinde tahsis edilen veya "sahip" göstericisi yığın üzerinde tahsis edilen nesneleri gösterir. Çalışma zamanı maliyeti olmadan derleme zamanı güvenliğini geliştirmek için işlevselliği (kasıtlı olarak) sınırlıdır. "Kapsam" işaretçilerinin amacı, esasen, hiçbir (çalışma zamanı) güvenlik mekanizmasının gerekli olmadığı kadar basit ve belirleyici olan bir dizi durumu belirlemektir.

mse::TRegisteredPointerHedef nesne yok edildiğinde değerinin otomatik olarak null_ptr olarak ayarlanması dışında ham bir işaretçi gibi davranır. Çoğu durumda ham işaretçiler için genel bir yedek olarak kullanılabilir. Ham bir işaretçi gibi, herhangi bir içsel iş parçacığı güvenliğine sahip değildir. Ancak bunun karşılığında, yığın üzerinde tahsis edilen nesneleri hedeflemede (ve ilgili performans avantajını elde etmede) hiçbir sorunu yoktur. Çalışma zamanı kontrolleri etkinleştirildiğinde, bu işaretçi geçersiz belleğe erişimden güvenlidir. Çünkü mse::TRegisteredPointergeçerli nesnelere işaret zaman ham işaretçi olarak aynı davranışa sahip, bunun debug / test yardım yakalamak hatalar için kullanılmasına izin, bir derleme zamanı yönergesi ile (otomatik olarak ilgili ham pointer ile değiştirilir) "engelli" olabilir / beta modları, yayınlama modunda genel maliyete neden olmaz.

İşte bu yüzden ve nasıl kullanılacakları açıklayan bir makale. (Not, utanmaz fiş.)

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.