2014-08-28 24 views
5

işaretine başvuru yapmak için temel sınıfın atamasını belirleme Şu anda polimorfik türler ve atama işlemleri arasındaki etkileşimi araştırıyorum. Benim asıl kaygım, birisinin bir temel sınıfın değerini türetilmiş bir sınıfın nesnesine tahsis etmeyi deneyip deneyemeyeceği, bu da sorunlara yol açıp açmayacağıdır.Türetilmiş sınıf

this answer'dan itibaren Temel sınıfın atama operatörü her zaman türetilen sınıfın örtük olarak tanımlanmış görev operatörü tarafından gizlendiğini öğrendim. Dolayısıyla, basit bir değişkene atanmak için yanlış tipler derleyici hatalarına neden olur.

class A { public: int a; }; 
class B : public A { public: int b; }; 
int main() { 
    A a; a.a = 1; 
    B b; b.a = 2; b.b = 3; 
    // b = a; // good: won't compile 
    A& c = b; 
    c = a; // bad: inconcistent assignment 
    return b.a*10 + b.b; // returns 13 
} 
atama Bu form muhtemelen nesne durumuna inconcistent yol açacak

Ancak hiçbir derleyici uyarısı yoktur ve kod ilk başta bana olmayan kötülük görünür: atama referans yoluyla oluşursa, ancak bu doğru değildir bakışta.

tür sorunları tespit etmek herhangi kurulan deyim var mı?

Ben böyle geçersiz atama bulursam ben sadece bir istisna atma, çalışma zamanı tespiti için umut sanırım. Şu anda düşünebildiğim en iyi yaklaşım, temel sınıftaki kullanıcı tanımlı bir yardımcı operatördür. Bu, this'un aslında türetilmiş bir sınıf için değil, bir temel örneğinin işaretçisi olduğundan emin olmak için çalışma zamanı türü bilgisini kullanır. el ile üye üye kopyası yapar. Bu, çok fazla yüke ve ciddi şekilde okunabilir kod okunabilirliğine benziyor. Daha kolay bir şey var mı?

Düzenleme: Bazı yaklaşımların uygulanabilirliği ben ne yapmak istediğinize bağlı gibi görünüyor beri, burada bazı detaylar. İki matematiksel kavramları, var

ring ve field söylerler. Her alan bir halkadır, ancak tersi değildir. Her biri için birkaç uygulama vardır ve bunlar ortak temel sınıfları, yani AbstractRing ve AbstractField'u paylaşırlar. Şimdi std::shared_ptr'a dayanarak kolay-yazımlı referans anlamlarını uygulamaya çalışıyorum. Bu yüzden benim Ring sınıfım, uygulamayı sürdüren bir std::shared_ptr<AbstractRing> ve buna yönlendiren bir grup yöntem içeriyor. Field'u Ring'dan devralmak için yazmak istiyorum, bu yüzden bu yöntemleri tekrarlamak zorunda kalmam. Bir alana özgü yöntemler, işaretçiyi AbstractField'a çevirirdi ve bunu statik olarak yapmak istiyorum. Ben işaretçi aslında inşaat bir AbstractField olduğunu garanti edebiliyor, ama birinin bu nedenle benim varsayılır değişmeyen hakkında içerdiği paylaşılan işaretçi kırarak, aslında bir Field bir Ring& bir Ring atar endişeliyim.

+0

Buradaki gerçek sorun, soyut olmayan bir temel sınıfınız yok mu? –

+1

Şahsen ben polimorfik türler için kopya oluşturucu ve atama işleçleri devre dışı bırakıyorum. Kalıtım tabanı polimorfizmi gerçekten değer tipleri ile iyi oynamamaktadır. –

+0

@OliCharlesworth: Nasıl görmüyorum. Örn. Bir düğmeden türetilen bir geçiş butonu, temel sınıfın somutlaşabileceği ve sorunun gerçek dünyada ortaya çıkabileceği durumları görebiliyorum. Bu nedenle, “tüm üslerin soyut olması” yaklaşımını takip etmem. Bu aklınızdaki gibi değilse, lütfen soyut temel sınıflarının durumumla nasıl yardımcı olabileceğini açıklayınız. – MvG

cevap

1

bir mahzun tip referansına atama derleme zamanında tespit edilemez beri dinamik bir çözüm öneriyoruz. Bu olağandışı bir durumdur ve genellikle buna karşı olurdum, ancak sanal atama operatörü kullanmak gerekebilir.

class Ring { 
    virtual Ring& operator = (const Ring& ring) { 
     /* Do ring assignment stuff. */ 
     return *this; 
    } 
}; 

class Field { 
    virtual Ring& operator = (const Ring& ring) { 
     /* Trying to assign a Ring to a Field. */ 
     throw someTypeError(); 
    } 

    virtual Field& operator = (const Field& field) { 
     /* Allow assignment of complete fields. */ 
     return *this; 
    } 
}; 

Bu muhtemelen en mantıklı yaklaşımdır.

Bunun bir alternatifi, bunun kaydını tutabilen ve basit işaretçiler * ve referansların & kullanımını yasaklayan referanslar için bir şablon sınıfı oluşturmak olabilir. Templated bir çözüm, doğru bir şekilde uygulamak için daha zor olabilir ancak downcast'i yasaklayan statik yazım denetimine izin verir. İşte benim için en azından benim için doğru bir derleme hatası verir "noDerivs (b)" ile GCC 4.8 ve -std = C++ 11 bayrağı (static_assert için), hatanın kaynağıdır.Kullanıcı ilk kendine ait bir referans oluşturur ve bir argüman olarak bu geçerse

#include <type_traits> 

template<class T> 
struct CompleteRef { 
    T& ref; 

    template<class S> 
    CompleteRef(S& ref) : ref(ref) { 
     static_assert(std::is_same<T,S>::value, "Downcasting not allowed"); 
     } 

    T& get() const { return ref; } 
    }; 

class A { int a; }; 
class B : public A { int b; }; 

void noDerivs(CompleteRef<A> a_ref) { 
    A& a = a_ref.get(); 
} 

int main() { 
    A a; 
    B b; 
    noDerivs(a); 
    noDerivs(b); 
    return 0; 
} 

Bu özel şablon

hala aptal olabilir. Sonunda, kullanıcılarınızı aptalca şeyler yapmaktan korumak umutsuz bir çabadır. Bazen tek yapabileceğiniz adil bir uyarı vermek ve ayrıntılı bir en iyi uygulama belgelerini sunmaktır.

+0

İlginç. “Çalışma zamanında bir downcast türü referansı atama yapılamıyor”: C++ 11'de 'ring :: operator = (…)' uygulamasının içinden 'typeid (* this) == typeid (Ring)' yapabilirsiniz aklımda olan şey buydu. Sanal operatörün daha iyi veya daha kötü bir performans gösterip göstermediğinden emin olun, bunu bir gün test etmek zorunda kalacaktı. “Bu, java'da standart atama yöntemidir.” Ancak Java'nın referans semantiği vardır, bu yüzden burada ne tür bir atıf yaptığınızı göremiyorum. Dizi üyelerinin atama yakın geliyor ama yine de bana farklı görünüyor. Bu Java yönü muhtemelen konu dışı, ama merak ediyorum. – MvG

+0

@MvG Tipik karşılaştırma, RTTI kullanılarak çalışma zamanında yapılır. Sanal atama, sınıf sanal tablosunu kullanır ve karşılaştırma gerektirmez. Genelde sanal üyeler C++ 'da tercih edilir, ancak RTTI' nın kıyaslamada önemli bir yük getirip getirmediğinden emin değilim. Sanal çağrı, tek-işaretli bir indirmedir, RTTI ise daha fazla (hiç de emin değil) içerebilir. Benim java biraz paslı. Atamadan ziyade Object .equals ve .clone'u kastettiğime inanıyorum, ancak benzer bir semantik var. – Zoomulator

+0

Sadece çalışma zamanı yazdığım zamanı derlediğimde fark ettim! – Zoomulator

İlgili konular