2016-06-19 110 views
5

ben, birden mirastan taban sınıflarına virtual yöntem ve işaretçileri beklemiyorduk bir sonuç var. d.getStr() ileÇoklu kalıtım, sanal metotlar çarpışma ve işaretçileri


, d bir derived örneğidir zaman beklendiği gibi base_2 sürümü denir. Beklendiği gibi p bir derived örneği (ya da derived örneğine işaret base_2 bir işaretçi) bir işaretçidir p->getStr() ile

, base_2 versiyonu, olarak adlandırılır.

Ama p bir derived örneğine işaret eden bir base_1 bir göstericidir p->getStr() ile

, base_1 sürümü denir ve ben base_2 versiyonunu aranmak ikna oldu (teşekkürler using ve getStr() virtual yöntemler olması) .

Aşağıdaki basit bir örnek:

#include <iostream> 

struct base_1 
{ 
    virtual std::string getStr() const 
    { return "string from base 1"; } 
}; 

struct base_2 
{ 
    virtual std::string getStr() const 
    { return "string from base 2"; } 
}; 

struct derived : public base_1, public base_2 
{ 
    using base_2::getStr; 
}; 


int main() 
{ 
    derived d; 

    derived * dp = &d; 
    base_1 * bp1 = &d; 
    base_2 * bp2 = &d; 

    std::cout << "from derived:   " << d.getStr() << std::endl; 
    std::cout << "from derived pointer: " << dp->getStr() << std::endl; 
    std::cout << "from base_1 pointer: " << bp1->getStr() << std::endl; 
    std::cout << "from base_2 pointer: " << bp2->getStr() << std::endl; 
} 

çıkışı

Ben base_2 versiyonunun çağrıyı empoze etmek, biliyoruz
from derived:   string from base 2 
from derived pointer: string from base 2 
from base_1 pointer: string from base 1 
from base_2 pointer: string from base 2 

, ben derived aşağıdaki yöntem de ekleyebilirsiniz aşağıdaki

std::string getStr() const 
{ return base_2::getStr(); } 

ama sorularım şunlardır:

1) Neden base_1 (türetilmiş bir örneğine işaret) için işaretçi using direktifini göz ardı edip getStr() ait base_1 versiyonunu diyor?

2) getStr() arasında base_2 versiyonu empoze bir yol var, derived örneği getStr() yeniden tanımlayarak olmadan base_1 işaretçi tarafından kullanılır?

--- DÜZENLEME --- cevapları için

teşekkürler.

Sana oluyor ama benim şüphe ne açıklayan anlıyoruz: Dil (standart) bu yönünü tarif ediyor? Yoksa tanımlanmamış bir bölüm mü?

Yani: Ne using yönergesi kaldırırsanız derleyici seçti getStr() hangi sürümünü bilmediği için, ben d.getStr() gelen ve dp->getStr() bir derleme hatası (error: request for member getStr is ambiguous), olsun.

Ama getStr()virtual yöntemlerdir. Yani (bunu ikna ettim) bir taban işaretçisinin türetilmiş sürümünü kullanması gerekir. Ama birkaç çarpışma metodumuz var.Görüş dil (standart) noktadan

, bir base_1 (veya base_2) işaretçi diğer görmezden çarpışan yöntemlerinin iki sürümlerinden birini seçmek için yetkili (veya yükümlü) nedir?

Belki yanılıyorsunuz ama bana göre virtual yöntemleri virtual yöntemleri olarak yönetiliyor.

+1

'using' yalnızca görünürlük içinde yardımcı olur. Temel sanal işlevden 'aşırı yüklenme' yapmaz veya ona benzemez. – Arunmu

cevap

0

1) Kodunuz tam olarak ne yapması gerektiği gibi görünüyor. Base_1'i işaret edersiniz, böylece base_1'den (veya temel sınıflarından) fonksiyonlar alırsınız. Nesnenin hem base_1 hem de base_2'den oluştuğu, bu noktada bilinmediğinden, türetilmiş bir sınıf değil, bir temel sınıfa işaret ettiniz. 2) Hayır, bu sadece imkansız. Gerçekten de, derived numaralı telefondan getStr() öğesini aşırı yüklemelisiniz.

struct derived : public base_1, public base_2 
{ 
    using base_2::getStr; 
}; 

bu aynıdır Yani:

+0

anser için teşekkürler, ancak nokta (1) hakkında, normal yöntemlerle temel/türetilmiş sınıfların davranışını tanımlıyorsunuz; 'getStr()' 'virtual' yöntemidir – max66

+0

Önemli değil; Gerçek şu ki, sanal fonksiyonlar yan yana olan sınıflardan "zıplayabiliyorlar", sadece bir sınıf-ilişki şeması çizerseniz dikey. – JvO

4

Sen şu şekilde using anahtar kelime kullanıldığında o bekliyoruz Bu durumda

struct derived : public base_1, public base_2 
{ 
    void getStr() 
    { 
     base_2::getStr(); 
    } 
}; 

, sen bekliyoruz davranış - - p, base_1 işaretçisiyse, p->getStr() çağırma - aslında base_2::getStr()'u çağırır. derived geçersiz kılar base_1 sitesindeki getStr(), yani çağırarak base_1 sitesindeki getStr(), normal bir işaretçi, derived sitesindeki getStr sonuçlanır() base_2getStr() yöntemini çağıran çağrılır elde. Bununla birlikte, bu gerçekleşmeyecek bir şey değildir. using anahtar sözcüğü, yöntem çağrısını bu şekilde iletmek için bir diğer ad değildir. using anahtar sözcüğü derived 'd sınıfında bir yöntem oluşturmaz, bu nedenle sınıf devralma etkilenmez ve derived_1' s getStr() altkümelerde geçersiz kılmamaktadır. Ve bu yüzden derived_1'un getStr() 'u çağırması, derived_2' un getStr() 'u çağırmasına neden olmaz.

2

Türetilen sınıfın, her bir temel sınıf için bir tane olmak üzere getStr() için 2 vtable girişi olması gerektiğinden, bu nedenle base_1::getStr() ve base_2::getStr() öğelerinin her ikisini de doğru şekilde çözebilir. using yönergesi, derived::getStr() vtable girişi oluşturmaz veya temel sınıf olanları değiştirmez, yalnızca hangi temel sınıf girişinin kullanılacağını seçer. base_1 için bir işaretçiden geçerken, derleyici yalnızca derived ve base_1 sanal işlevler için vtable girişlerini "görür" ve böylece getStr() değerini base_1::getStr() olarak çözer. 'da açık bir getStr() ekleme çözümünüz muhtemelen en temiz olanıdır, ancak açıklık için temel sınıfların eşleşmesi sanal olarak önerilebilir.

+0

Cevabınız için teşekkürler; benim şüphem: türetilmiş sınıftaki 2 veteriner C++ standardı tarafından empoze edilir veya bir uygulama detayıdır?Başka bir deyişle: Bu, standart tarafından dayattığı için mi yoksa başka derleyicilerin farklı bir davranış üretmeye yetkili olduğundan mı? – max66

+0

Mekaniği bir uygulama detayıdır, ancak temel sınıfların kendileri, sanal yöntemlerle türetilmiş sınıflar olabileceğinden, sonuçların kesin mekanizması ne olursa olsun, hemen hemen aynı olması gerekir. Tek alternatif, derleyicinin tüm kodu görmesi ve işaret edilen nesnenin gerçek tipinin ne olduğunu anlayabilmesidir, ki bu son derece önemsizdir. –