2011-11-16 28 views
5

sto leggendo di eredità e ho un grosso problema che non sono stato in grado di risolvere per ore:eredità virtuale Confusione

Data una classe Bar è una classe con virtual funzioni,

class Bar 
{ 
    virtual void Cook(); 
}; 

Qual è la differenza tra:

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

e

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

? Ore di googlatura e lettura hanno fornito molte informazioni sui suoi usi, ma nessuno in realtà mi dice quale sia la differenza tra i due e mi confondano di più.

+2

Non ho intenzione di rispondere perché l'argomento non merita davvero un trattamento così superficiale: ma senza 'virtuale' ogni classe che eredita da' Bar' avrà la propria copia di 'Bar', con' virtuale' la classe più derivata avrà solo una copia di 'Bar'. –

+0

Prova: [questa ricerca] (http://stackoverflow.com/search?q = virtuale + ereditarietà +% 5Bc% 2B% 2B% 5D) –

+0

possibile duplicato di [In classe base virtuale C++?] (http://stackoverflow.com/questions/21558/in-c-virtual-base-class) –

risposta

4

L'ereditarietà virtuale è rilevante solo se le classi ereditano da Foo.Se io definisco il seguente:

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

Poi l'oggetto finale conterrà una sola copia del B, condivisa da entrambe L e R. Senza lo virtual, un oggetto di tipo D conterrà due copie di B, uno in L e uno in R.

C'è qualche argomento che tutte le eredità dovrebbe essere virtuale (perché nei casi in cui si fa la differenza, questo è ciò che si desidera la maggior parte del del tempo). In pratica, tuttavia, l'ereditarietà virtuale è costosa e, nella maggior parte dei casi, non è necessario : in un sistema ben progettato, la maggior parte dell'eredità sarà semplicemente di una classe concreta che eredita da una o più interfacce " "; una classe così concreta di solito non è progettata per essere derivata da se stessa, quindi non ci sono problemi. Ma ci sono importanti eccezioni: se, ad esempio, si definisce un'interfaccia, e poi estensioni per l'interfaccia, le estensioni devono ereditare praticamente dall'interfaccia di base, dal momento che una concreta attuazione potrebbe voler implementare diverse estensioni. O se stai progettando mixin, dove le classi implementano solo parte dell'interfaccia e la classe finale eredita da diverse di queste classi (una per parte dell'interfaccia ). Alla fine, il criterio da quanto alla possibilità di ereditare praticamente o no, non è troppo difficile:

  • se l'eredità non è pubblica, probabilmente non dovrebbe essere virtuale (non ho mai visto un'eccezione), altrimenti

  • se la classe non è progettato per essere una classe base, non c'è bisogno di eredità virtuale, altrimenti

  • l'eredità dovrebbe essere virtuale.

Ci sono alcune eccezioni, ma le regole di cui sopra sbagliare sul lato della sicurezza; di solito è "corretto" ereditare praticamente anche nei casi in cui l'ereditarietà virtuale non è necessaria.

Un punto finale: una base virtuale deve sempre essere inizializzato dalla classe più derivata, non classe che eredita direttamente (e dichiara che l'eredità è virtuale). In pratica, tuttavia, questo è un non-problema. Se si considerano i casi in cui l'ereditarietà virtuale ha senso, è sempre che eredita da un'interfaccia, che non conterrà dati e quindi avrà (solo) un costruttore predefinito. Se ti ritrovi a ereditando virtualmente da classi con costruttori che accettano argomenti , è ora di fare alcune domande serie sul design.

5

Funzionalità saggia non c'è molta differenza tra le 2 versioni. Con il caso dell'ereditarietà virtual, ogni implementazione generalmente aggiunge un puntatore (vptr simile) (come nel caso delle funzioni virtual). Che aiuta ad evitare copie multiple classi base generate a causa di ereditarietà multipla (il problema) diamond inheritance

Inoltre, virtual eredità delegati il ​​diritto di chiamare il costruttore della sua classe base. Ad esempio,

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

Così, ora Bar::Bar() saranno chiamati direttamente da Other::Other() e anche sarà posizionato al primo posto tra le altre classi base.

Questo delegazione funzione aiuta nella realizzazione di una funzionalità final class (in Java) in C++ 03:

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

In questo caso, nessuna differenza. eredità virtuale è legata alle istanze di condivisione superclasse oggetti secondari da classi derivate

struct A 
{ 
    int a; 
}; 

struct B : public virtual A 
{ 
    int b; 
} 

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

struct D : public B, public C 
{ 
}; 

C'è una singola copia della variabile membro a nel caso di D; Se A non era una classe base virtuale, ci sarebbero due sottooggetti A in istanza di D.

+0

"_In questo caso, nessuna differenza._" finché non provi ad usare 'static_cast'! – curiousguy

0

La funzione virtuale è una funzione che probabilmente avrà un'implementazione diversa nella classe derivata (sebbene non sia obbligatoria).

Nel tuo ultimo esempio è l'ereditarietà virtuale. Immagina un caso in cui hai due classi (A e B) derivate da una classe base (chiamiamola 'Base'). Ora immagina una terza classe C derivata da A e B. Senza ereditarietà virtuale, la C conterrebbe due copie di 'Base'. Ciò potrebbe portare ad ambiguità durante la compilazione. La cosa importante nell'ereditarietà virtuale è che i parametri per il costruttore della classe 'Base' (se ce ne sono) DEVONO essere forniti nella classe C, poiché tali chiamate da A e B saranno ignorate.

+0

"_Questo potrebbe portare ad ambiguità durante la compilazione._" questo ** ci condurrà ad un'ambiguità _iff_ tu provi 'C' IS-A' Base'. – curiousguy