2009-07-28 16 views
8

Perché si consiglia di non avere membri dati nella classe base virtuale?Membri dati classe base virtuale

E i membri delle funzioni? Se si dispone di un'attività comune a tutte le classi derivate, è OK per la classe base virtuale eseguire l'attività o l'ereditarietà derivata da due classi classificate - dall'interfaccia virtuale e dalla base semplice che eseguono l'attività?

Grazie.

+6

Potresti fornire un link o una citazione per il consiglio? vedere molto punto nel contrassegnare una classe base virtuale se non ha membri dati, poiché lo scopo più comune dell'ereditarietà virtuale è impedire la duplicazione dei membri della classe base. Per esempio nelle librerie standard, ios_base ha membri dati, ed è unclasse base virtuale (via ios) di entrambi i istream e ostream. Quindi direi quasi (ma non proprio) il contrario: se hai intenzione di avere una classe base virtuale, allora dovrebbe avere membri dei dati, o ereditare non virtualmente. –

risposta

11

Come pratica si dovrebbe usare l'ereditarietà virtuale per definire le interfacce in quanto sono di solito utilizzati con l'ereditarietà multipla per garantire che solo una versione della classe è presente nella classe derivata. E le interfacce pure sono la forma più sicura di ereditarietà multipla. Naturalmente se sai cosa stai facendo puoi usare l'eredità multipla come preferisci, ma può risultare in un codice fragile se non stai attento.

Il più grande svantaggio con l'ereditarietà virtuale è se i loro costruttori accettano parametri. Se devi passare parametri al costruttore di una classe base virtuale, imponi a tutte le classi derivate di chiamare esplicitamente il costruttore (non possono fare affidamento su una classe base che chiama il costruttore).

L'unico motivo che posso vedere per il vostro avviso esplicito è che i dati nella vostra classe base virtuale potrebbero richiedere parametri di costruzione.

Modifica Ho fatto un po 'di lavoro a casa dopo il commento di Martin, grazie a Marin. La prima linea non è del tutto vero:

Come pratica si dovrebbe usare eredità virtuale per definire interfacce in quanto sono di solito utilizzati con l'ereditarietà multipla per garantire che solo una versione della classe è presente nella classe derivata.

L'ereditarietà virtuale non fa alcuna differenza se la classe base è un'interfaccia pura (eccetto per errori del compilatore leggermente diversi, in vc8, se tutti i metodi non sono implementati). Si fa solo una reale differenza, se la classe base contiene dati, in questo caso si finisce con un diamante piuttosto che una figura di U

Non virtual virtual 
    A  A   A 
    |  |  / \ 
    B  C  B  C 
    \ /  \ /
    D    D 

Nel caso virtuale, B e C condividono la stessa copia della A.

Tuttavia sono ancora d'accordo con tutto il resto sulle interfacce pure che sono la forma più sicura di ereditarietà multipla, anche se non richiedono l'ereditarietà virtuale. E il fatto che i parametri del costruttore e l'ereditarietà virtuale siano un dolore.

+0

+1 Questo è anche riassunto in C++ FAQ Lite http://www.new-brunswick.net/workshop/c++/faq/multiple-inheritance.html#faq-25.8 –

+0

Non credo che ciò sia vero. –

+0

@Martin: purtroppo, è ... –

0

a) i membri dovrebbero essere privati ​​- così si può ottenere i problemi che li utilizzano nelle classi derivate (in modo da avere per aggiungere metodi getter e setter, che soffiano la vostra interfaccia)

b) non dichiarare cose che voi al momento non utilizzare - dichiarare variabili solo nelle classi che l'accesso/li

c) utilizzare le classi base virtuali dovrebbero contenere solo le interfacce/metodi virtuali, niente di più

mi auguro che aiuta un po ', anche se le mie ragioni non sono perfette e complete :)

Ciao, Chris

1

Non ho mai visto questa raccomandazione.

Una classe è un insieme di funzioni e dati strettamente correlati. Lo scopo di una classe base è avere un insieme comune di funzioni e dati che possono essere riutilizzati per classi derivate.

Penso che limitarsi a non avere membri di dati o funzioni virtuali non pure nelle classi base ridurrà la quantità di riutilizzo del codice, che porta a un codice meno affidabile nel lungo periodo.

+1

Se riesci a ottenere una copia di "Effective C++", leggi l'articolo 20: "Evita i membri dei dati nelle interfacce pubbliche". – quark

+0

Giusto, sono d'accordo con questo. Non sto suggerendo che i membri dei dati siano pubblici. In ogni caso, ho frainteso la domanda e penso che la risposta di iain sia buona. –

2

Il consiglio di base è di avere un costruttore di default nella base virtuale. Se non lo fai, allora ogni classe più derivata da (es. Ogni sottoclasse) deve chiamare il ctor base virtuale in modo esplicito, e che porta a colleghi arrabbiati bussare alla tua porta dell'ufficio ...

class VirtualBase { 
public: 
    explicit VirtualBase(int i) : m_i(i) {} 
    virtual ~VirtualBase() {} 

private: 
    int m_i; 
}; 

class Derived : public virtual VirtualBase { 
public: 
    Derived() : VirtualBase(0) {} // ok, this is to be expected 
}; 

class DerivedDerived : public Derived { // no VirtualBase visible 
public: 
    DerivedDerived() : Derived() {} // ok? no: error: need to explicitly 
            // call VirtualBase::VirtualBase!! 
    DerivedDerived() : VirtualBase(0), Derived() {} // ok 
}; 
+0

Vuol dire che se non avessi chiamato il virtual base ctor esplicitamente non verrà chiamato quando creerò l'oggetto derivato? – jackhab

+0

Devi _quello_ chiamare il ctor della classe base virtuale nell'implementazione di _each_ derivata del ctor, il compilatore non ti permetterà di farla franca altrimenti. L'unica eccezione: se la base virtuale ha un ctor predefinito, quello viene chiamato quando non ne chiami esplicitamente un altro. –

0

Tutto Le classi di base rapide utilizzate per simulare le interfacce in C++ non devono avere membri di dati, poiché descrivono un'interfaccia e lo stato di istanza è un dettaglio di implementazione.

Oltre a ciò, le classi di base contenenti funzioni virtuali potrebbero avere membri di dati. Si applicano tuttavia le normali regole:

  • renderle il più nascoste possibile e utilizzare getter e setter se è necessario conservare gli invarianti di classe.
    (C'è una scappatoia qui: se non c'è alcuna invarianza associata al membro, cioè può assumere qualsiasi valore possibile in qualsiasi momento, è possibile renderlo pubblico.Tuttavia, questo nega una classe derivata aggiungendo un invariante
  • Design per l'ereditarietà: il contratto della classe dovrebbe definire le responsabilità e le possibilità di una classe derivata, di cosa ha bisogno/può sovrascrivere per quale scopo
Problemi correlati