shared_ptr büyü :)


91

Bay Lidström ve ben tartıştık :)

Bay Lidström'ün iddiası, bir yapının Base'in shared_ptr<Base> p(new Derived);sanal bir yıkıcıya sahip olmasını gerektirmediğidir:

Armen Tsirunyan : "Gerçekten Will Shared_ptr ? Eğer etkileyen nasıl yansıdığını gösteren bu durumda lütfen Could doğru temizlemek"

Daniel Lidström : " shared_ptr , Concrete örneğini silmek için kendi yıkıcısını kullanır. Bu, C ++ topluluğu içinde RAII olarak bilinir. Benim tavsiyem, RAII hakkında öğrenebileceğiniz her şeyi öğrenmenizdir. Kullandığınızda C ++ kodlamanızı çok daha kolay hale getirecektir. Her durumda RAII. "

Armen Tsirunyan : " RAII'yi biliyorum ve sonunda, paylaşılan_ptr yıkıcısının, pn 0'a ulaştığında depolanan px'i silebileceğini de biliyorum. Ama eğer px statik tip işaretçisi Baseve dinamik tip işaretçisi varsa Derived, o zaman Basesanal bir yıkıcı yoksa , bu tanımlanmamış davranışlara neden olur. Hatalıysam düzeltin. "

Daniel Lidström : " shared_ptr statik tipin Beton olduğunu biliyor. Bunu yapıcısında geçtiğimden beri biliyor! Biraz sihir gibi görünüyor, ancak tasarım gereği ve son derece güzel olduğunu size temin ederim."

Bizi yargıla. Polimorfik sınıfların sanal yıkıcıya sahip olmasını gerektirmeden paylaşılan_tr'yi uygulamak (eğer öyleyse) nasıl mümkün olabilir ? Şimdiden teşekkürler


3
Orijinal başlığa bağlanabilirdin .
Darin Dimitrov

8
Bir başka ilginç şey shared_ptr<void> p(new Derived)de, Derivednesneyi yıkıcı tarafından, olsun virtualya da olmasın yok edecek olmasıdır.
dalle

7
Soru sormanın harika yolu :)
rubenvb

5
Shared_ptr buna izin verse de, bir sınıfı sanal bir yönlendirici olmadan temel olarak tasarlamak gerçekten kötü bir fikirdir . Daniel'in RAII hakkındaki yorumları yanıltıcıdır - bununla hiçbir ilgisi yoktur - ancak alıntı yapılan konuşma basit bir yanlış iletişim (ve shared_ptr'nin nasıl çalıştığına dair yanlış varsayım) gibi geliyor.

6
RAII değil, yıkıcıyı yazarak siler. Sen çünkü dikkatli olmak zorunda shared_ptr<T>( (T*)new U() )nerede struct U:Tdoğru olanı yapmak olmaz (ve bu tür bir alan bir fonksiyonu olarak, dolaylı kolaylıkla yapılabilir T*ve bir geçirilir U*)
Yakk - Adam Nevraumont

Yanıtlar:


74

Evet, bu şekilde shared_ptr uygulamak mümkündür. Boost yapar ve C ++ 11 standardı da bu davranışı gerektirir. Ek bir esneklik olarak shared_ptr bir referans sayacından daha fazlasını yönetir. Sözde silme, genellikle referans sayaçlarını da içeren aynı bellek bloğuna yerleştirilir. Ancak işin eğlenceli yanı, bu silicinin türünün shared_ptr türünün bir parçası olmamasıdır. Bu, "tür silme" olarak adlandırılır ve temelde "polimorfik işlevler" boost :: işlevi veya std :: işlevini gerçek işlevin türünü gizlemek için uygulamak için kullanılan teknikle aynıdır. Örneğinizin işe yaraması için şablonlu bir kurucuya ihtiyacımız var:

template<class T>
class shared_ptr
{
public:
   ...
   template<class Y>
   explicit shared_ptr(Y* p);
   ...
};

Yani, bunu Base ve Derived sınıflarınızla kullanırsanız ...

class Base {};
class Derived : public Base {};

int main() {
   shared_ptr<Base> sp (new Derived);
}

... Y = Türetilmiş şablonlu yapıcı, shared_ptr nesnesini oluşturmak için kullanılır. Yapıcı böylece uygun silme nesnesini ve referans sayaçlarını yaratma şansına sahiptir ve bu kontrol bloğuna bir gösterici bir veri üyesi olarak depolar. Referans sayacı sıfıra ulaşırsa, nesneyi elden çıkarmak için önceden oluşturulmuş ve Türetilmiş farkında olan silici kullanılacaktır.

C ++ 11 standardı, bu kurucu hakkında şunları söyler (20.7.2.2.1):

Gerektirir: p dönüştürülebilir olmalıdır T*. Ytam bir tip olacaktır. İfade delete piyi biçimlendirilecek, iyi tanımlanmış davranışa sahip olacak ve istisnalar yaratmayacaktır.

Efektler: İşaretçiye sahip olan bir shared_ptrnesne oluşturur .p

Ve yıkıcı için (20.7.2.2.2):

Etkileri: Eğer *thisbir boş diğeriyle veya hisse sahiplik shared_ptrörneğine ( use_count() > 1), hiçbir yan etkileri vardır. Aksi takdirde, *thisbir nesnenin sahibi pve bir deleter d, d(p)denir. Aksi takdirde, *thisbir işaretçiye sahipse pve delete pçağrılırsa.

(kalın yazı tipini vurgulamak benimdir).


the upcoming standard also requires this behaviour: (a) Hangi standart ve (b) lütfen bir referans (standarda) sunabilir misiniz?
kevinarpe

Yeterli puanım olmadığı için @ sellibitze'nin cevabına bir yorum eklemek istiyorum add a comment. IMO, daha Boost does thisfazlasıdır the Standard requires. Standart'ın anladığım kadarıyla bunu gerektirdiğini sanmıyorum. @Sellibitze 'ın örnek hakkında konuşmak shared_ptr<Base> sp (new Derived);, Gerektirir ait constructorsadece istemek delete Derivedolmanın iyi tanımlanmış ve iyi oluşturulmuş. Şartnamesi destructoriçin ayrıca bir var p, ancak pşartnamesinde belirtildiğini sanmıyorum constructor.
Lujun Weng

28

Shared_ptr oluşturulduğunda, kendi içinde bir silme nesnesi depolar . Bu nesne, shared_ptr işaretli kaynağı serbest bırakmak üzereyken çağrılır. Kaynağın yapım aşamasında nasıl yok edileceğini bildiğiniz için, tamamlanmamış türlerle shared_ptr kullanabilirsiniz. Shared_ptr'yi her kim yarattıysa orada doğru bir siler.

Örneğin, özel bir silici oluşturabilirsiniz:

void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.

shared_ptr<Base> p(new Derived, DeleteDerived);

p, sivri uçlu nesneyi yok etmek için DeleteDerived'i çağırır. Uygulama bunu otomatik olarak yapar.


4
Eksik türlerle ilgili açıklama için +1, shared_ptrbir özellik olarak kullanıldığında çok kullanışlıdır .
Matthieu M.

16

Basitçe,

shared_ptr Base'in yıkıcısını değil, her zaman verilen nesnenin yıkıcısını kullanan kurucu tarafından oluşturulan özel silme işlevini kullanır; bu, şablon meta programlamayla biraz çalışma gerektirir, ancak işe yarar.

Bunun gibi bir şey

template<typename SomeType>
shared_ptr(SomeType *p)
{
   this->destroyer = destroyer_function<SomeType>(p);
   ...
}

1
hmm ... ilginç, buna inanmaya başlıyorum :)
Armen Tsirunyan

1
@Armen Tsirunyan Discusson'ı başlatmadan önce shared_ptr'nin tasarım açıklamasına göz atmış olmalısınız. Bu 'silenin yakalanması', shared_ptr'nin temel özelliklerinden biridir ...
Paul Michalik

6
@ paul_71: Sana katılıyorum. Öte yandan, bu tartışmanın sadece benim için değil, aynı zamanda shared_ptr hakkındaki bu gerçeği bilmeyen diğer insanlar için de faydalı olduğuna inanıyorum. Sanırım bu konuya başlamak büyük bir günah değildi :)
Armen Tsirunyan

3
@Armen Elbette hayır. Aksine, deneyimli c ++ geliştiricileri tarafından bile sıklıkla denetlenen bu gerçekten çok çok önemli olan shared_ptr <T> özelliğine işaret etmekte iyi bir iş çıkardınız.
Paul Michalik
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.