The C++ Compass
|
>FAQ<
|
Home Risorse Utilities Compilatori GUI Toolkits Libri Download FAQ Who am I |
Quando conviene usare una classe base virtuale ?
Sostanzialmente quando, in eredità multipla, si vuole una "identità" unica fra classi che se no sarebbero disparate. Ecco un semplice esempio didattico basato sull'idioma "mix-in". Supponiamo di avere una classe base astratta, tipo: class emittore { public: virtual void emetti(const std::string&) = 0; }; le implementazioni di questa incapsulano il concetto di "emettere una riga" (magari la scrivono con iostream, magari la mandano ad un log di sistema, e/o la mostrano in una finestra su schermo, e così via). Il modo più normale per connettere del codice "cliente" alla implementazione concreta di questa classe è passare al cliente un puntatore a emittore, a cui delegherà in modo esplicito l'output: class chiaccherone { emittore* pEm; public: chiacchierone(emittore* pEm): pEm(pEm) {} void parla() { pEm->emetti("Ciao!"); // 998 linee omesse pEm->emetti("OK, basta."); } }; da usarsi ad esempio con: // si suppone: class emittore_su_stream: public emittore ... emittore_su_stream ess(std::cout); chiacchierone ciac(&ess); ciac.parla(); Supponiamo, però, che per qualche buona e valida ragione scegliamo invece di fare la connessione con l'uso dell'eredità multipla. Allora, dovremo usare emittore come base virtuale: class emittore_su_stream: public virtual emittore { // ecc ecc class chiacchierone_2: public virtual emittore { public: void parla() { emetti("Ciao!"); // 998 linee omesse emetti("OK, basta."); } }; class cliente_connettore: public emittore_su_stream , public chiacchierone_2 { public: cliente_connettore(std::iostream& x): emittore_su_stream(x) {} }; Adesso, l'uso diverrà: cliente_connettore x(std::cout); x.parla(); Come vedi, l'eredità multipla (con base virtuale) ottiene risultati analoghi alla connessione esplicita ma con una fusione di identità fra 'cliente' dell'emittore (il 'chiacchierone') e 'servente' della stessa base astratta (l'emittore_su_stream). Normalmente non è una gran bella alternativa perchè si riduce la flessibilità, ma a volte è proprio questo "fissaggio compile-time" quello che desideriamo (soprattutto se vogliamo parametrizzarlo in un template). Che la base virtuale sia una base astratta pura (una "interfaccia", o come alcuni anche dicono [secondo me falsando un poco la terminologia più usuale] una "classe-protocollo") è molto tipico; è possibile costruire esempi in cui ha senso avere una base virtuale che non e` puramente astratta (sostanzialmente per una scelta di centralizzare uno "stato" -- ma normalmente, questo "stato" se ne starebbe meglio in una classe mixin, con i suoi accessori nella base astratta pura che fa da base virtuale), ma si tratta, così a ocio, di meno del 5-10% dei casi reali di uso di questo costrutto già un pò astruso. |
webmaster@thecppcompass.org |