2011-11-16 33 views
5

Ben miras hakkında okuyorum ve ben saatlerce çözmek mümkün olmamıştır önemli bir sorun vardır:Sanal Kalıtım Karışıklık

bir sınıf Bar virtual fonksiyonları ile bir sınıftır Verilen

,

class Bar 
{ 
    virtual void Cook(); 
}; 

class Foo : public Bar 
{ 
    virtual void Cook(); 
}; 

ve

01: arasında farklı nedir

? Googling ve okuma saatleri, kullanımları hakkında pek çok bilgi birikimiyle ortaya çıktı, ancak hiçbiri ikisinin arasındaki farkın ne olduğunu söylemiyor.

+2

Cevap vermeyeceğim çünkü konu bu tür sığ tedaviyi gerçekten hak etmiyor: "sanal" olmadan "Bar" öğesinden devralan her sınıf, "Sanal" ile kendi "Bar" kopyasına sahip olacak En çok türetilen sınıfın "Bar" ın yalnızca bir kopyası olacaktır. –

+0

Deneyin: [bu arama] (http://stackoverflow.com/search?q = sanal + devralma +% 5Bc% 2B% 2B% 5D) –

+0

[In C++ sanal temel sınıfının olası kopyası?] (http://stackoverflow.com/questions/21558/in-c-virtual-base-class) –

cevap

4

Sanal kalıtım yalnızca sınıflar Foo'dan miras kalacaksa geçerlidir.Ben şunları tanımlarsanız:

class B {}; 
class L : virtual public B {}; 
class R : virtual public B {}; 
class D : public L, public R {}; 

Ardından nihai nesne yalnızca her iki L ve R tarafından paylaşılan B bir kopyasını içerecektir. virtual olmadan, D türünde bir nesne, , R'dan biri olan B'un iki kopyası içerir.

tüm miras sanal gereken bazı argüman yoktur (bir fark yaratıyor durumlarda çünkü o size zaman içinde en çok istediği şeydir). Bununla birlikte, pratikte sanal kalıtım pahalıdır ve çoğu durumda gerekli değildir: iyi tasarlanmış bir sistemde, çoğu kalıtımı, bir veya bir daha "arayüz" den gelen bir somut sınıftan olacaktır; Böyle bir beton sınıfı genellikle kendisinden türetilen olarak tasarlanmamıştır, bu yüzden sorun yoktur. Ancak önemli istisnalar vardır: Örneğin, bir arabirimi tanımlarsanız ve arabirime uzantıları tanımlarsanız, uzantılar temel arabirimden miras almalıdır, çünkü somut bir uygulama çeşitli uzantıları uygulamak isteyebilir. Veya, belirli sınıfların yalnızca arabirimin bir kısmını uyguladığı karışımlar tasarlıyorsanız ve son sınıfı, bu sınıfların birkaçından ( arabiriminin bir parçası) birinden devralınır. miras kamu değilse

  • , muhtemelen sanal olmamalı (hiç görmedim: Sonunda, olmadığı konusunda criteron neredeyse ya da değil çok zor değildir miras bir istisna), sınıf bir temel sınıf olarak tasarlanmış değilse aksi

  • , aksi

  • miras sanal olmalı, sanal miras gerek yoktur.

Orada birkaç istisna vardır, ancak yukarıdaki kuralları güvenliği elden bırakmıyoruz; 'un sanal mirasın gerekli olmadığı durumlarda bile sanal olarak bile miras almak genellikle “doğru” dur.

Bir son nokta: sanal bir temel zaman değildir direkt olarak miras (ve miras sanal olduğunu beyan eder) sınıfı, en türetilmiş sınıf tarafından başlatılmalıdır. Pratikte ise, bu bir sorun değil. Sanal devralmanın mantıklı olduğu durumlara bakarsanız, her zaman bir arabirimden miras alma, veri içermeyen ve dolayısıyla (yalnızca) bir varsayılan kurucuya sahip olan bir duruma bakar. Kendinizi buluyorsanız, argümanlarını alan yapıcılarla sınıflardan sanal olarak miras alıyorsanız, tasarım hakkında bazı ciddi sorular sorma zamanı gelmiştir.

5

İşlevsellik açısından, 2 versiyon arasında çok fazla fark yoktur. virtual devralma durumunda, her uygulama genellikle bir (vptr benzeri) işaretçisi ekler (virtual işlevlerinde olduğu gibi). Hangi birden verasetle (diamond inheritance sorunu) nedeniyle oluşturulan birden çok temel sınıf kopya Ayrıca

, virtual miras delegeler kendi temel sınıf yapıcısı aramakla doğru önlenmesine yardımcı olur. Örneğin,

class Bar; 
class Foo : public virtual Bar 
class Other : public Foo // <--- one more level child class 

Yani, şimdi Bar::Bar()Other::Other() doğrudan çağrılır ve ayrıca diğer temel sınıflar arasında ilk yerde yer alacaktır.

özellik C++ 03'te bir final class (Java) işlevselliği uygulanmasında yardımcı olan bu heyet: Bu durumda

class Final { 
    Final() {} 
    friend class LastClass; 
}; 

class LastClass : virtual Final { // <--- 'LastClass' is not derivable 
... 
}; 

class Child : public LastClass { // <--- not possible to have object of 'Child' 
}; 
3

, hiçbir fark. Sanal miras türetilmiş sınıfları

struct A 
{ 
    int a; 
}; 

struct B : public virtual A 
{ 
    int b; 
} 

struct C : public virtual A 
{ 
    int c; 
}; 

struct D : public B, public C 
{ 
}; 

D örneğinde üye değişkeni a tek kopyası var paylaştığını üst sınıfının alt nesneler örneklerine ilgilidir; A bir sanal taban sınıfı değilse, D örneğinde iki A alt nesnesi olacaktır.

+0

"Bu durumda, hiç fark değilsiniz", "static_cast" kullanmaya çalışana kadar! – curiousguy

0

Sanal işlev, muhtemelen türetilmiş sınıfta farklı uygulamalara sahip olan bir işlevdir (zorunlu olmamasına rağmen).

Son örneğinizde sanal devralmadır. Bir temel sınıftan türetilen iki sınıfın (A ve B) bulunduğu bir durumu düşünün (ona 'Base' diyelim). Şimdi A ve B'den türetilmiş bir üçüncü sınıf C düşünün. Sanal devralma olmadan, C iki “Base” kopyasını içerecektir. Bu derleme yaparken belirsizliğe yol açabilir. Sanal kalıtımda önemli olan şey, eğer 'Temel' sınıf kurucusunun (eğer varsa) parametrelerinin C sınıfında sağlanması ZORUNLUDUR, çünkü A ve B'den gelen çağrılar göz ardı edilecektir.

+0

"_Bu derleme sırasında belirsizliğe yol açabilir" Bu ** bir belirsizliğe yol açacaktır _iff_ 'C' IS-A 'Tabanı'nı deneyin. – curiousguy