2010-08-22 16 views
11

(perdono la domanda novellino in anticipo)C++: ereditarietà multipla con polimorfismo

Ho 4 classi:

class Person {}; 
class Student : public Person {}; 
class Employee : public Person {}; 
class StudentEmployee : public Student, public Employee {}; 

Essenzialmente Person è la classe di base, che sono direttamente sottoclasse sia Student e Employee. StudentEmployee utilizza l'ereditarietà multipla per sottoclasse sia Student e Employee.

Person pat = Person("Pat"); 
Student sam = Student("Sam"); 
Employee em = Employee("Emily"); 
StudentEmployee sen = StudentEmployee("Sienna"); 


Person ppl[3] = {pat, sam, em}; 
//compile time error: ambiguous base class 
//Person ppl[4] = {pat, sam, em, sen}; 

Quando uso un array di Person, la classe di base, che può mettere Person e tutte le sue sottoclassi all'interno di questa matrice. Tranne StudentEmployee, data la ragione per la classe base ambigua.

Dato che StudentEmployee è garantito per avere tutti i metodi e gli attributi di Person, è StudentEmployee considerato una sottoclasse di Persona?

  • In tal caso, perché il compilatore non mi consente di assegnare un oggetto a una variabile del tipo della sua superclasse?
  • In caso contrario, perché no; e quale sarebbe il modo corretto per realizzare questo?

Acclamazioni


EDIT: preventivamente, questa domanda non è lo stesso di uno dei seguenti:
polymorphism relates inheritance
Inheritance mucking up polymorphism in C++?

risposta

13

StudentEmployee è sicuramente una sottoclasse di Person. Il problema è che è così due volte: eredita indirettamente Person due volte (una volta tramite Student e una volta tramite Employee) ed è per questo che si ottiene l'errore "classe base ambigua". Per assicurarsi StudentEmployee eredita solo Person una volta, è necessario utilizzare eredità virtuale, in questo modo:

class Person {}; 
class Student : public virtual Person {}; 
class Employee : public virtual Person {}; 
class StudentEmployee : public Student, public Employee {}; 

Questo sarà risolvere il tuo errore.

C'è un altro grosso problema con il codice, però, e si chiama slicing.

Quando si esegue questa operazione:

Person ppl[3] = {pat, sam, em}; 

sarà creata una rete di tre Person oggetti, ma questi oggetti sarà copiare costruito utilizzando il costruttore di copia definito implicitamente della classe Person. Ora, il problema con questo è che gli oggetti nell'array saranno solo oggetti Person e non oggetti delle sottoclassi che si desidera siano.

Per risolvere questo problema, si dovrà fare un array di puntatori a Person oggetti, in questo modo:

Person* ppl[] = {new Person("Pat"), new Student("Sam"), 
       new Employee("Emily"), new StudentEmployee("Sienna")}; 

o

Person* ppl[] = {&pat, &sam, &em, &sen}; 
+0

Ma, ovviamente, chiamare nuove del genere quasi certamente porterà a una perdita di memoria – Falmarri

+1

+1 per l'affettamento. E nel caso in cui bguiz non lo sapesse già, potrebbe anche essere Person * ppl [] = {& pat, & sam, & em, &sen }; - c'è una leggera differenza nella gestione della memoria di queste due opzioni. – Michael

+0

@Falmarri: perché? Hai ancora la serie di puntatori. – Job

3

Esistono due ugualmente possibili percorsi da un oggetto di digitare StudentEmployee per essere un Person.

È necessario utilizzare la parola chiave virtual per entrambe le classi Student e Employee. Vedi FAQ 25.8 In effetti passa attraverso l'intera sezione.

+0

Grazie, che l'articolo ha colpito il chiodo sulla testa ! – bguiz