Shared_ptr <Temel>, shared_ptr <Derived> 'e indiriliyor mu?


103

Güncelleme: Bu örnekteki shared_ptr, Boost'takine benzer, ancak shared_polymorphic_downcast'i (veya bu konuda dynamic_pointer_cast veya static_pointer_cast'i) desteklemez!

Referans sayısını kaybetmeden türetilmiş bir sınıfa paylaşılan bir işaretçi başlatmaya çalışıyorum:

struct Base { };
struct Derived : public Base { };
shared_ptr<Base> base(new Base());
shared_ptr<Derived> derived;

// error: invalid conversion from 'Base* const' to 'Derived*'
derived = base;  

Çok uzak çok iyi. C ++ 'nın Base *' i dolaylı olarak Derived * 'e dönüştürmesini beklemiyordum. Bununla birlikte, kod tarafından ifade edilen işlevselliği istiyorum (yani, temel işaretçiyi aşağıya doğru çevirirken referans sayımını sürdürmek). İlk düşüncem, Derived'e örtük bir dönüşümün gerçekleşebilmesi için Base'de bir döküm operatörü sağlamaktı (bilgiçler için: Aşağı atmanın geçerli olup olmadığını kontrol ederdim, endişelenmeyin):

struct Base {
  operator Derived* ();
}
// ...
Base::operator Derived* () {
  return down_cast<Derived*>(this);
}

Pek yardımcı olmadı. Görünüşe göre derleyici typecast operatörümü tamamen görmezden geldi. Shared_ptr atamasını nasıl çalıştırabileceğime dair bir fikriniz var mı? Ekstra puan için: ne tür bir tür Base* const? const Base*Anlıyorum ama Base* const? constBu durumda neyi ifade eder?


Neden shared_ptr <Base> yerine bir shared_ptr <Derived> gerekiyor?
Bill

3
Çünkü nesneyi klonlamadan, Base'de olmayan Derived'deki işlevselliğe erişmek istiyorum (iki paylaşılan işaretçi tarafından başvurulan tek bir nesne istiyorum). Bu arada, oyuncu kadrosu neden çalışmıyor?
Lajos Nagy

Yanıtlar:


111

Kullanabilirsiniz dynamic_pointer_cast. Tarafından desteklenmektedir std::shared_ptr.

std::shared_ptr<Base> base (new Derived());
std::shared_ptr<Derived> derived =
               std::dynamic_pointer_cast<Derived> (base);

Belgeler: https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

Ayrıca, temel sınıfta cast operatörü kullanmanızı önermiyorum. Bunun gibi örtük çevrim, hataların ve hataların kaynağı olabilir.

-Güncelleme: Tür polimorfik değilse std::static_pointer_castkullanılabilir.


4
Kullanmadığını ilk satırdan anlamadım std::shared_ptr. Ancak ilk cevabın yorumlarından, boost kullanmadığı sonucuna vardım, bu yüzden kullanıyor olabilir std::shared_ptr.
Massood Khaari

TAMAM. Afedersiniz. Özel bir uygulama kullandığını daha iyi açıklamalıdır.
Massood Khaari

47

Kullandığını varsayıyorum boost::shared_ptr... Sanırım dynamic_pointer_castya da istiyorsun shared_polymorphic_downcast.

Ancak bunlar polimorfik tipler gerektirir.

tip ne tür Base* constnedir? const Base*Anlıyorum ama Base* const? constBu durumda neyi ifade eder?

  • const Base *bir sabite değiştirilebilir bir göstericidir Base.
  • Base const *bir sabite değiştirilebilir bir göstericidir Base.
  • Base * constbir değişkene sabit bir göstericidir Base.
  • Base const * constsabit bir göstericidir Base.

İşte minimal bir örnek:

struct Base { virtual ~Base() { } };   // dynamic casts require polymorphic types
struct Derived : public Base { };

boost::shared_ptr<Base> base(new Base());
boost::shared_ptr<Derived> derived;
derived = boost::static_pointer_cast<Derived>(base);
derived = boost::dynamic_pointer_cast<Derived>(base);
derived = boost::shared_polymorphic_downcast<Derived>(base);

Örneğinizin temel türün bir örneğini oluşturup yayınladığının kasıtlı olup olmadığından emin değilim, ancak farkı güzel bir şekilde göstermeye hizmet ediyor.

static_pointer_castİrade "sadece yap". Bu, tanımlanmamış davranışla sonuçlanacaktır ( Derived*tahsis edilen ve tarafından başlatılan belleğe işaret etme Base) ve muhtemelen bir çökmeye veya daha kötüsüne neden olacaktır. Referans sayısı baseartacaktır.

dynamic_pointer_castBir boş gösterici ile sonuçlanacaktır. Referans sayısı basedeğişmeyecektir.

shared_polymorphic_downcastStatik bir döküm olarak aynı sonucu verecektir, bunun yerine başarılı olmak görünüşteki ve tanımsız davranış oluşmasına neden yerine, bir onaylama tetikleyecektir. Referans sayısı baseartacaktır.

Bakın (ölü bağlantı) :

Bazen kullanmaya karar vermek biraz zor static_castya dynamic_castve her iki dünyanın biraz var isterdim. Dynamic_cast'in bir çalışma zamanı ek yüküne sahip olduğu iyi bilinir, ancak daha güvenlidir, oysa static_cast hiçbir ek yüke sahip değildir, ancak sessizce başarısız olabilir. shared_dynamic_castHata ayıklama yapılarında ve shared_static_castsürüm yapılarında kullanabilseydiniz ne kadar güzel olurdu . Eh, böyle bir şey zaten mevcut ve deniyor shared_polymorphic_downcast.


Maalesef, çözümünüz kullandığımız belirli shared_ptr uygulamasından kasıtlı olarak hariç tutulan Boost işlevine bağlıdır (nedenini sormayın). Sabit açıklamaya gelince, şimdi çok daha mantıklı.
Lajos Nagy

3
Diğer shared_ptrkurucuları uygulamak ( static_cast_tagve alarak dynamic_cast_tag) dışında, yapabileceğiniz pek bir şey yok. Dışarıda shared_ptryapacağınız hiçbir şey yeniden sayımı yönetemeyecektir. - "Mükemmel" bir OO tasarımında her zaman temel türü kullanabilirsiniz ve türetilmiş türün ne olduğunu bilmenize veya önemsemenize gerek kalmaz, çünkü tüm işlevselliği temel sınıf arabirimler aracılığıyla ortaya çıkar. Belki de ilk etapta neden alt kademe atmanız gerektiğini yeniden düşünmeniz gerekiyor.
Tim Sylvester

1
@Tim Sylvester: ama, C ++ "mükemmel" bir OO dili değil! :-) aşağı yayınların yeri mükemmel olmayan bir OO dilinde yer alıyor
Steve Folly

4

Biri buraya boost :: shared_ptr ile gelirse ...

Türetilmiş Boost shared_ptr'ye bu şekilde aşağıya inebilirsiniz. Derived'in Base'den miras aldığını varsayarsak.

boost::shared_ptr<Base> bS;
bS.reset(new Derived());

boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS);
std::cout << "DerivedSPtr  is: " << std::boolalpha << (dS.get() != 0) << std::endl;

'Base' sınıf / yapının en az bir sanal işleve sahip olduğundan emin olun. Sanal bir yıkıcı da çalışır.

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.