Perché dovrei evitare l'ereditarietà multipla in C ++?


Risposte:


259

Odori di ereditarietà multipla (abbreviati come MI) , il che significa che di solito è stato fatto per cattive ragioni e tornerà indietro di fronte al manutentore.

Sommario

  1. Considera la composizione delle funzionalità, anziché l'ereditarietà
  2. Diffidare del diamante del terrore
  3. Considerare l'ereditarietà di più interfacce anziché di oggetti
  4. A volte, l'ereditarietà multipla è la cosa giusta. Se lo è, allora usalo.
  5. Preparati a difendere la tua architettura a eredità multipla nelle revisioni del codice

1. Forse composizione?

Questo è vero per l'eredità e quindi è ancora più vero per l'eredità multipla.

Il tuo oggetto deve davvero ereditare da un altro? A Carnon ha bisogno di ereditare da a Engineper lavorare, né da a Wheel. A Carha un Enginee quattro Wheel.

Se usi l'ereditarietà multipla per risolvere questi problemi anziché la composizione, allora hai fatto qualcosa di sbagliato.

2. Il diamante del terrore

Di solito, hai una classe A, quindi Bed Centrambi erediti da A. E (non chiedetemi perché) qualcuno decide allora che Ddeve ereditare sia da Be C.

Ho riscontrato questo tipo di problema due volte in 8 eight anni ed è divertente vederlo a causa di:

  1. Quanto è stato un errore dall'inizio (in entrambi i casi, Dnon avrebbe dovuto ereditare da entrambi Be C), perché questa era una cattiva architettura (in realtà, Cnon avrebbe dovuto esistere affatto ...)
  2. Quanti manutentori pagavano per questo, perché in C ++, la classe genitore Aera presente due volte nella sua classe nipote De, quindi, aggiornare un campo genitore A::fieldsignificava aggiornarlo due volte (attraverso B::fielde C::field), o avere qualcosa che andava silenziosamente storto e si arrestava in modo anomalo, in seguito (nuovo un puntatore in B::field, ed elimina C::field...)

L'uso della parola chiave virtuale in C ++ per qualificare l'eredità evita il doppio layout sopra descritto se questo non è quello che vuoi, ma comunque, nella mia esperienza, probabilmente stai facendo qualcosa di sbagliato ...

Nella gerarchia degli oggetti, dovresti provare a mantenere la gerarchia come un albero (un nodo ha UN genitore), non come un grafico.

Maggiori informazioni sul diamante (modifica 2017-05-03)

Il vero problema con Diamond of Dread in C ++ ( supponendo che il design sia valido - fai rivedere il tuo codice! ), È che devi fare una scelta :

  • È auspicabile che la classe Aesista due volte nel tuo layout, e cosa significa? Se sì, allora eredita da esso due volte.
  • se dovrebbe esistere solo una volta, quindi ereditarlo virtualmente.

Questa scelta è inerente al problema e in C ++, a differenza di altre lingue, puoi effettivamente farlo senza dogma costringendo il tuo progetto a livello linguistico.

Ma come tutti i poteri, da quel potere derivano responsabilità: fai rivedere il tuo progetto.

3. Interfacce

L'ereditarietà multipla di zero o una delle classi concrete e zero o più interfacce di solito va bene, perché non incontrerai il Diamante del terrore descritto sopra. In effetti, è così che si fanno le cose in Java.

Di solito, cosa intendi quando C eredita da Aed Bè che gli utenti possono usare Ccome se fosse un A, e / o come se fosse un B.

In C ++, un'interfaccia è una classe astratta che ha:

  1. tutto il suo metodo dichiarato puro virtuale (suffisso da = 0) (rimosso il 05-05-2017)
  2. nessuna variabile membro

L'eredità multipla da zero a un oggetto reale e zero o più interfacce non è considerata "puzzolente" (almeno, non tanto).

Ulteriori informazioni sull'interfaccia astratta C ++ (modifica 2017-05-03)

Innanzitutto, il modello NVI può essere utilizzato per produrre un'interfaccia, poiché il vero criterio è di non avere uno stato (ovvero nessuna variabile membro, eccetto this). Il punto della tua interfaccia astratta è pubblicare un contratto ("puoi chiamarmi in questo modo e in questo modo"), niente di più, niente di meno. La limitazione di avere solo un metodo virtuale astratto dovrebbe essere una scelta progettuale, non un obbligo.

In secondo luogo, in C ++, ha senso ereditare virtualmente da interfacce astratte (anche con il costo aggiuntivo / indiretto). Se non lo fai, e l'ereditarietà dell'interfaccia appare più volte nella tua gerarchia, allora avrai ambiguità.

In terzo luogo, l'orientamento agli oggetti è eccezionale, ma non è The Only Truth Out There TM in C ++. Usa gli strumenti giusti e ricorda sempre di avere altri paradigmi in C ++ che offrono diversi tipi di soluzioni.

4. Hai davvero bisogno dell'ereditarietà multipla?

A Volte si.

Di solito, la tua Cclasse eredita da Ae B, e Ae Bsono due oggetti non correlati (cioè non nella stessa gerarchia, niente in comune, concetti diversi, ecc.).

Ad esempio, potresti avere un sistema Nodescon coordinate X, Y, Z, in grado di eseguire molti calcoli geometrici (forse un punto, parte di oggetti geometrici) e ogni nodo è un agente automatizzato, in grado di comunicare con altri agenti.

Forse hai già accesso a due librerie, ognuna con il proprio spazio dei nomi (un altro motivo per usare gli spazi dei nomi ... Ma tu usi gli spazi dei nomi, vero?), Un essere geoe l'altro essereai

In modo da avere il proprio own::Nodederivano sia da ai::Agente geo::Point.

Questo è il momento in cui dovresti chiederti se non dovresti usare la composizione. Se own::Nodeè davvero sia a ai::Agentche a geo::Point, la composizione non lo farà.

Quindi avrai bisogno dell'ereditarietà multipla, facendo own::Nodecomunicare con altri agenti in base alla loro posizione in uno spazio 3D.

(Noterai che ai::Agente geo::Pointsono completamente, totalmente, completamente SENZA RELAZIONE ... Ciò riduce drasticamente il pericolo di eredità multipla)

Altri casi (modifica 2017-05-03)

Ci sono altri casi:

  • usando l'eredità (si spera privata) come dettaglio di implementazione
  • alcuni modi di dire C ++ come le politiche potrebbero usare l'ereditarietà multipla (quando ogni parte deve comunicare con le altre attraverso this)
  • l'eredità virtuale da std :: exception (l' ereditarietà virtuale è necessaria per le eccezioni? )
  • eccetera.

A volte puoi usare la composizione, a volte l'MI è migliore. Il punto è: hai una scelta. Fallo responsabilmente (e fai rivedere il tuo codice).

5. Quindi, dovrei fare l'ereditarietà multipla?

Il più delle volte, nella mia esperienza, no. L'MI non è lo strumento giusto, anche se sembra funzionare, perché può essere utilizzato dai pigri per raggruppare le funzionalità senza rendersene conto (come fare Carsia una Engineche una Wheel).

Ma a volte sì. E a quel tempo, niente funzionerà meglio di MI.

Ma poiché MI è maleodorante, preparati a difendere la tua architettura nelle revisioni del codice (e difenderla è una buona cosa, perché se non sei in grado di difenderla, non dovresti farlo).


4
Direi che non è mai necessario e così raramente utile che, anche se si adatta perfettamente, i risparmi sulla delega non compensano la confusione per i programmatori non abituati a usarlo, ma per il resto un buon riassunto.
Bill K,

2
Aggiungerei al punto 5, che il codice che utilizza MI dovrebbe avere un commento accanto ad esso che spiega il motivo, quindi non è necessario spiegarlo verbalmente in una revisione del codice. Senza questo, qualcuno che non è il tuo revisore può mettere in dubbio il tuo buon senso quando vede il tuo codice e potresti non avere l'opportunità di difenderlo.
Tim Abell,

" perché non incontrerai il diamante del terrore sopra descritto. " Perché no?
curioso

1
Uso l'MI per il modello di osservatore.
Calmarius,

13
@BillK: Certo che non è necessario. Se disponi di una lingua di conversione completa senza MI, puoi fare qualsiasi cosa che una lingua con MI possa fare. Quindi sì, non è necessario. Mai. Detto questo, può essere uno strumento molto utile, e il cosiddetto Dreaded Diamond ... Non ho mai veramente capito perché la parola "temuto" sia persino lì. In realtà è abbastanza facile ragionare e di solito non è un problema.
Thomas Eding,

145

Da un'intervista a Bjarne Stroustrup :

Le persone affermano correttamente che non hai bisogno dell'ereditarietà multipla, perché tutto ciò che puoi fare con l'ereditarietà multipla può anche fare con l'eredità singola. Usa solo il trucco della delegazione che ho citato. Inoltre, non hai bisogno di alcuna eredità, perché qualsiasi cosa tu faccia con una singola eredità puoi anche fare a meno dell'eredità inoltrando attraverso una classe. In realtà, non hai bisogno di nessuna classe, perché puoi fare tutto con puntatori e strutture di dati. Ma perché dovresti farlo? Quando è conveniente usare le strutture linguistiche? Quando preferiresti una soluzione alternativa? Ho visto casi in cui l'ereditarietà multipla è utile e ho persino visto casi in cui l'ereditarietà multipla piuttosto complicata è utile. In generale, preferisco utilizzare le strutture offerte dalla lingua per risolvere il problema


6
Ci sono alcune caratteristiche di C ++ che mi sono mancate in C # e Java, anche se averle renderebbe il design più difficile / complicato. Ad esempio, le garanzie di immutabilità di const- ho dovuto scrivere soluzioni complicate (di solito usando interfacce e composizione) quando una classe aveva davvero bisogno di avere varianti mutabili e immutabili. Tuttavia, una volta non ho mai perso l'eredità multipla e non ho mai sentito di dover scrivere una soluzione alternativa a causa della mancanza di questa funzionalità. Questa è la differenza In ogni caso che abbia mai visto, non usare MI è la scelta di progettazione migliore, non una soluzione alternativa.
BlueRaja - Danny Pflughoeft,

24
+1: risposta perfetta: se non si desidera utilizzarlo, non utilizzarlo.
Thomas Eding,

38

Non c'è motivo di evitarlo e può essere molto utile in situazioni. Devi essere consapevole dei potenziali problemi però.

Il più grande è il diamante della morte:

class GrandParent;
class Parent1 : public GrandParent;
class Parent2 : public GrandParent;
class Child : public Parent1, public Parent2;

Ora hai due "copie" di GrandParent in Child.

C ++ ci ha pensato e ti permette di fare l'ereditarietà virtuale per aggirare i problemi.

class GrandParent;
class Parent1 : public virtual GrandParent;
class Parent2 : public virtual GrandParent;
class Child : public Parent1, public Parent2;

Rivedi sempre il tuo progetto, assicurati di non utilizzare l'ereditarietà per risparmiare sul riutilizzo dei dati. Se puoi rappresentare la stessa cosa con la composizione (e in genere puoi farlo), questo è un approccio molto migliore.


21
E se escludi eredità e unica composizione uso, invece, si ha sempre due GrandParentin Child. C'è il timore dell'MI, perché le persone pensano solo che potrebbero non capire le regole del linguaggio. Ma chiunque non riesca a ottenere queste semplici regole non può nemmeno scrivere un programma non banale.
curioso

1
Sembra duro, le regole del C ++ sono molto elaborate. OK, quindi anche se le singole regole sono semplici, ce ne sono molte, il che rende il tutto non semplice, almeno per un umano da seguire.
Zebrafish,

11

Vedi w: Eredità multipla .

L'eredità multipla ha ricevuto critiche e, come tale, non è implementata in molte lingue. Le critiche includono:

  • Maggiore complessità
  • L'ambiguità semantica è spesso sintetizzata come il problema del diamante .
  • Non essere in grado di ereditare esplicitamente più volte da una singola classe
  • Ordine di ereditarietà che cambia la semantica della classe.

L'ereditarietà multipla nei linguaggi con costruttori in stile C ++ / Java aggrava il problema dell'ereditarietà dei costruttori e del concatenamento dei costruttori, creando così problemi di manutenzione ed estensibilità in questi linguaggi. Gli oggetti nelle relazioni di ereditarietà con metodi di costruzione molto diversi sono difficili da implementare sotto il paradigma del concatenamento del costruttore.

Modo moderno di risolverlo per usare l'interfaccia (pura classe astratta) come l'interfaccia COM e Java.

Posso fare altre cose al posto di questo?

Si, puoi. Ho intenzione di rubare da GoF .

  • Programma per un'interfaccia, non un'implementazione
  • Preferisce la composizione rispetto all'eredità

8

L'eredità pubblica è una relazione IS-A, e talvolta una classe sarà un tipo di diverse classi diverse, a volte è importante riflettere questo.

I "mixin" sono talvolta utili. Sono generalmente classi piccole, che di solito non ereditano da nulla, fornendo funzionalità utili.

Finché la gerarchia dell'ereditarietà è abbastanza superficiale (come dovrebbe quasi sempre essere) e ben gestita, è improbabile che tu abbia la temuta eredità del diamante. Il diamante non è un problema con tutte le lingue che usano l'ereditarietà multipla, ma il suo trattamento con C ++ è spesso imbarazzante e talvolta sconcertante.

Mentre ho incontrato casi in cui l'ereditarietà multipla è molto utile, in realtà sono piuttosto rari. Ciò è probabilmente perché preferisco utilizzare altri metodi di progettazione quando non ho davvero bisogno dell'ereditarietà multipla. Preferisco evitare costrutti linguistici confusi, ed è facile costruire casi di ereditarietà in cui devi leggere molto bene il manuale per capire cosa sta succedendo.


6

Non dovresti "evitare" l'eredità multipla, ma devi essere consapevole dei problemi che possono sorgere come il "problema dei diamanti" ( http://en.wikipedia.org/wiki/Diamond_problem ) e trattare il potere che ti è stato dato con cura , come dovresti con tutti i poteri.


1
+1: proprio come non dovresti evitare i puntatori; devi solo essere consapevole delle loro ramificazioni. Le persone hanno bisogno di smettere di scuotere frasi (come "MI è cattivo", "non ottimizzare", "goto è male") che hanno imparato da Internet solo perché alcuni hippy hanno detto che era dannoso per la loro igiene. La parte peggiore è che non hanno nemmeno mai provato a usare queste cose e le prendono come se fossero state dette dalla Bibbia.
Thomas Eding,

1
Sono d'accordo. Non "evitarlo", ma conosci il modo migliore per gestire il problema. Forse preferirei usare tratti compositivi, ma C ++ non ce l'ha. Forse preferirei usare la delega automatizzata "handle", ma C ++ non lo possiede. Quindi uso lo strumento generale e smussato di MI per diversi scopi, forse contemporaneamente.
JDługosz,

3

A rischio di diventare un po 'astratto, trovo illuminante pensare all'eredità nel quadro della teoria delle categorie.

Se pensiamo a tutte le nostre classi e frecce tra loro che indicano relazioni di eredità, allora qualcosa del genere

A --> B

significa che class Bderiva da class A. Si noti che, dato

A --> B, B --> C

diciamo che C deriva da B che deriva da A, quindi si dice che anche C derivi da A, quindi

A --> C

Inoltre, diciamo che per ogni classe da Acui Aderiva banalmente A, quindi il nostro modello di eredità soddisfa la definizione di una categoria. In un linguaggio più tradizionale, abbiamo una categoria Classcon oggetti tutte le classi e morfismi le relazioni di eredità.

Questo è un po 'di installazione, ma con ciò diamo un'occhiata al nostro Diamond of Doom:

C --> D
^     ^
|     |
A --> B

È un diagramma dall'aspetto ombroso, ma lo farà. Quindi, Deredita da tutti A, Be C. Inoltre, e avvicinandosi ad affrontare la domanda di OP, Deredita anche da qualsiasi superclasse di A. Possiamo disegnare un diagramma

C --> D --> R
^     ^
|     |
A --> B
^ 
|
Q

Ora, i problemi associati al Diamante della Morte qui sono quando Ce Bcondividere alcuni nomi di proprietà / metodi e le cose diventano ambigue; tuttavia, se spostiamo qualsiasi comportamento condiviso in Aallora l'ambiguità scompare.

Mettiamo in termini categorici, vogliamo A, Bed Cessere tale che se Be Cerediti da Qallora Apossano essere riscritti come sottoclasse di Q. Questo rende Aqualcosa chiamato pushout .

C'è anche una costruzione simmetrica Dchiamata pullback . Questa è essenzialmente la classe utile più generale che puoi costruire che eredita da entrambi Be C. Cioè, se hai qualche altra classe che si Rmoltiplica ereditando da Be C, allora Dè una classe dove Rpuò essere riscritta come sottoclasse di D.

Accertarsi che le punte del diamante siano pullback e pushout ci dà un buon modo per gestire genericamente problemi di scontro o manutenzione che potrebbero sorgere altrimenti.

Nota la risposta di Paercebal ha ispirato questo perché le sue ammonizioni sono implicate nel modello sopra dato che lavoriamo nella categoria completa di tutte le classi possibili.

Volevo generalizzare la sua argomentazione a qualcosa che mostra quanto complicate relazioni di eredità multiple possano essere sia potenti che non problematiche.

TL; DR Pensa alle relazioni di eredità nel tuo programma come a formare una categoria. Quindi puoi evitare i problemi di Diamond of Doom facendo pushout di classi ereditate in modo multiplo e simmetricamente, creando una classe genitore comune che è un pullback.


3

Usiamo Eiffel. Abbiamo un ottimo IM. Nessun problema. Senza problemi. Facilmente gestibile. Ci sono momenti in cui NON usare MI. Tuttavia, è utile più di quanto le persone si rendano conto perché sono: A) in un linguaggio pericoloso che non lo gestisce bene -OPP-) soddisfatto di come hanno lavorato con IM per anni e anni -OP- C) altri motivi ( sono troppo numeroso per elencarlo, ne sono abbastanza sicuro - vedi le risposte sopra).

Per noi, utilizzando Eiffel, MI è naturale come qualsiasi altra cosa e un altro strumento eccellente nella cassetta degli attrezzi. Francamente, non ci preoccupiamo del fatto che nessun altro stia usando Eiffel. Nessun problema. Siamo contenti di ciò che abbiamo e ti invitiamo a dare un'occhiata.

Mentre stai guardando: prendi nota della sicurezza del Vuoto e dell'eradicazione della dereferenziazione del puntatore Null. Mentre balliamo tutti intorno a MI, i tuoi puntatori si stanno perdendo! :-)


2

Ogni linguaggio di programmazione ha un trattamento leggermente diverso della programmazione orientata agli oggetti con pro e contro. La versione di C ++ pone l'accento sulle prestazioni e ha il rovescio della medaglia che è inquietantemente facile scrivere codice non valido - e questo vale per l'ereditarietà multipla. Di conseguenza si tende a allontanare i programmatori da questa funzione.

Altre persone hanno affrontato la questione di ciò che l'ereditarietà multipla non è utile. Ma abbiamo visto alcuni commenti che implicano più o meno che il motivo per evitarlo è perché non è sicuro. Bene, sì e no.

Come spesso accade in C ++, se segui una linea guida di base puoi usarla in sicurezza senza dover "guardarti alle spalle" costantemente. L'idea chiave è che si distingue un tipo speciale di definizione di classe chiamato "mix-in"; class è un mix-in se tutte le sue funzioni membro sono virtuali (o pure virtuali). Quindi puoi ereditare da una singola classe principale e da tutti i "mix-in" che desideri, ma dovresti ereditare i mixin con la parola chiave "virtuale". per esempio

class CounterMixin {
    int count;
public:
    CounterMixin() : count( 0 ) {}
    virtual ~CounterMixin() {}
    virtual void increment() { count += 1; }
    virtual int getCount() { return count; }
};

class Foo : public Bar, virtual public CounterMixin { ..... };

Il mio suggerimento è che se intendi utilizzare una classe come classe mista, adotti anche una convenzione di denominazione per rendere più facile a chiunque legga il codice vedere cosa sta succedendo e verificare che stai giocando secondo le regole della linea guida di base . E scoprirai che funziona molto meglio se i tuoi mix-in hanno anche costruttori predefiniti, proprio per il modo in cui funzionano le classi di base virtuali. E ricorda di rendere virtuali anche tutti i distruttori.

Nota che il mio uso della parola "mix-in" qui non è lo stesso della classe template parametrizzata (vedi questo link per una buona spiegazione) ma penso che sia un uso corretto della terminologia.

Ora non voglio dare l'impressione che questo sia l'unico modo per usare l'ereditarietà multipla in sicurezza. È solo un modo che è abbastanza facile da controllare.


2

Dovresti usarlo con attenzione, ci sono alcuni casi, come il problema del diamante , quando le cose possono andare complicate.

testo alternativo
(fonte: learncpp.com )


12
Questo è un cattivo esempio, perché una fotocopiatrice dovrebbe essere composta da uno scanner e una stampante, non ereditati da essi.
Svante,

2
Comunque, a Printernon dovrebbe nemmeno essere a PoweredDevice. A Printerè per la stampa, non per la gestione dell'alimentazione. L'implementazione di una particolare stampante potrebbe dover comportare un po 'di risparmio energetico, ma questi comandi di risparmio energetico non dovrebbero essere esposti direttamente agli utenti della stampante. Non riesco a immaginare alcun uso del mondo reale di questa gerarchia.
curioso


1

Oltre al modello a diamante, l'ereditarietà multipla tende a rendere il modello a oggetti più difficile da comprendere, il che a sua volta aumenta i costi di manutenzione.

La composizione è intrinsecamente facile da capire, comprendere e spiegare. Può essere noioso scrivere codice per, ma un buon IDE (sono passati alcuni anni da quando ho lavorato con Visual Studio, ma sicuramente gli IDE Java hanno tutti ottimi strumenti di automazione dei collegamenti di composizione) dovrebbe farti superare questo ostacolo.

Inoltre, in termini di manutenzione, il "problema del diamante" emerge anche in casi di eredità non letterali. Ad esempio, se hai A e B e la tua classe C li estende entrambi, e A ha un metodo "makeJuice" che produce succo d'arancia e lo estendi per produrre succo d'arancia con un tocco di lime: cosa succede quando il designer per " B 'aggiunge un metodo' makeJuice 'che genera e corrente elettrica? 'A' e 'B' possono essere "genitori" compatibili in questo momento , ma ciò non significa che lo saranno sempre!

Nel complesso, la massima tendenza a evitare l'ereditarietà, e in particolare l'ereditarietà multipla, è solida. Come tutte le massime, ci sono eccezioni, ma devi assicurarti che ci sia un'insegna al neon verde lampeggiante che punta a tutte le eccezioni che codifichi (e allena il tuo cervello in modo che ogni volta che vedi tali alberi ereditari disegni nel tuo neon verde lampeggiante segno) e che controlli per assicurarti che tutto abbia senso una volta ogni tanto.


what happens when the designer for 'B' adds a 'makeJuice' method which generates and electrical current?Uhhh, ricevi ovviamente un errore di compilazione (se l'uso è ambiguo).
Thomas Eding,

1

Il problema chiave con l'MI di oggetti concreti è che raramente hai un oggetto che dovrebbe legittimamente "Essere un A AND essere un B", quindi raramente è la soluzione corretta per motivi logici. Molto più spesso, hai un oggetto C che obbedisce a "C può agire come una A o una B", che puoi ottenere tramite ereditarietà e composizione dell'interfaccia. Ma non commettere errori: l'ereditarietà di più interfacce è ancora MI, solo un sottoinsieme di esso.

Per il C ++ in particolare, il punto debole della funzionalità non è l'effettiva ESISTENZA dell'ereditarietà multipla, ma alcuni costrutti che consente sono quasi sempre malformati. Ad esempio, ereditando più copie dello stesso oggetto come:

class B : public A, public A {};

è malformato DA DEFINIZIONE. Tradotto in inglese, questo è "B è una A e una A". Quindi, anche nel linguaggio umano c'è una grave ambiguità. Intendevi "B ha 2 come" o semplicemente "B è una A" ?. Consentire tale codice patologico, e peggio renderlo un esempio di utilizzo, C ++ non ha favorito quando si trattava di presentare un caso per mantenere la funzionalità nei linguaggi successivi.


0

Puoi usare la composizione in preferenza dell'ereditarietà.

La sensazione generale è che la composizione sia migliore ed è molto ben discussa.


4
-1: The general feeling is that composition is better, and it's very well discussed.Ciò non significa che la composizione sia migliore.
Thomas Eding,

Solo che, in realtà, è meglio.
Ali Afshar,

12
Tranne i casi in cui non è meglio.
Thomas Eding

0

richiede 4/8 byte per classe coinvolta. (Uno questo puntatore per classe).

Questo potrebbe non essere mai un problema, ma se un giorno hai una struttura di microdati che viene istanziata miliardi di volte, lo sarà.


1
Sicuro? In genere, qualsiasi eredità seria richiede un puntatore in ciascun membro della classe, ma è necessariamente scalabile con le classi ereditate? Più precisamente, quante volte hai avuto miliardi di strutture dati per adolescenti?
David Thornley,

3
@DavidThornley " Quante volte hai avuto miliardi di strutture dati per adolescenti? " Quando stai discutendo contro l'MI.
curioso
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.