Domanda

Ho una classe base astratta che funge da interfaccia.

Ho due "set" di classi derivate, che implementano metà della classe astratta. (uno "set" definisce i metodi virtuali astratti relativi all'inizializzazione, l'altro "set" definisce quelli relativi all'effettivo "lavoro".)

Ho quindi classi derivate che usano l'ereditarietà multipla per costruire classi completamente definite (e non aggiungono nulla da sé).

Quindi: (pseudocodice errato)

class AbsBase {
  virtual void init() = 0;
  virtual void work() = 0;
}

class AbsInit : public AbsBase {
  void init() { do_this(); }
  // work() still abs
}

class AbsWork : public AbsBase {
  void work() { do_this(); }
  // init() still abs
}

class NotAbsTotal : public AbsInit, public AbsWork {
  // Nothing, both should be defined
}

Prima di tutto, posso farlo? Posso ereditare da due classi che sono entrambe derivate dalla stessa Base? (Lo spero).

Ecco il "problema reale", tuttavia (ho mentito un po 'sopra per semplificare l'esempio).

Quello che ho davvero fatto è stato aggiungere metodi di accesso non astratti alla classe base:

class AbsBase {
public:
  void init() { init_impl(); }
  void work() { work_impl(); }

private:
  virtual void init_impl() = 0;
  virtual void work_impl() = 0;
}

Perché, un linguaggio comune è rendere privati ??tutti i metodi virtuali.

Sfortunatamente, ora sia AbsInit che AbsWork ereditano questi metodi, quindi NotAbsTotal eredita "due di ciascuno" (Mi rendo conto che potrei macellare ciò che sta realmente accadendo in fase di compilazione).

Ad ogni modo, g ++ lamenta che: "la richiesta del membro init () è ambigua" quando si tenta di utilizzare la classe.

Suppongo che, se avessi usato la mia classe AbsBase come un'interfaccia pura, questo sarebbe stato evitato (supponendo che l'esempio principale sia valido).

Quindi: - Sono fuori strada con la mia implementazione? - È una limitazione del linguaggio di rendere privati ??i metodi virtuali? - Come posso refactificare il mio codice per fare quello che voglio? (Fornisci un'interfaccia comune, ma consenti di scambiare le implementazioni con "insiemi" di funzioni membro)

Modifica:

Sembra che io non sia il primo: http://en.wikipedia.org/wiki/Diamond_problem

Sembra che l'ereditarietà virtuale sia la soluzione qui. Ho sentito parlare dell'eredità virtuale prima, ma non ci ho avvolto la testa. Sono ancora aperto ai suggerimenti.

È stato utile?

Soluzione

Sembra che tu voglia fare l'eredità virtuale. Se questa sia effettivamente una buona idea è un'altra domanda, ma ecco come lo fai:


class AbsBase {...};
class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};

Fondamentalmente, l'ereditarietà multipla non virtuale predefinita includerà una copia di ogni classe di base nella classe derivata e include tutti i loro metodi. Questo è il motivo per cui hai due copie di AbsBase e la ragione per cui il tuo metodo di utilizzo è ambiguo è che sono caricati entrambi i set di metodi, quindi C ++ non ha modo di sapere a quale copia accedere!

L'ereditarietà virtuale condensa tutti i riferimenti a una classe di base virtuale in una struttura dati. Ciò dovrebbe rendere di nuovo inequivocabili i metodi della classe base. Tuttavia, nota: se ci sono dati aggiuntivi nelle due classi intermedie, potrebbe esserci qualche piccolo sovraccarico aggiuntivo di runtime, per consentire al codice di trovare la classe di base virtuale condivisa.

Altri suggerimenti

Devi dichiarare l'eredità come virtuale:

struct AbsBase {
          virtual void init() = 0;
          virtual void work() = 0;
};

struct AbsInit : virtual public AbsBase {
          void init() {  }
};

struct AbsWork : virtual public AbsBase {
          void work() { }
};

struct NotAbsTotal : virtual public AbsInit, virtual public AbsWork {
};

void f(NotAbsTotal *p)
{
        p->init();
}

NotAbsTotal x;

Può essere fatto, sebbene dia la maggior parte dei brividi.

Devi utilizzare " ereditarietà virtuale " ;, la sintassi per cui è qualcosa di simile

class AbsInit: public virtual AbsBase {...};
class AbsWork: public virtual AbsBase {...};
class NotAbsTotal: public AbsInit, public AbsWork {...};

Quindi devi specificare quale funzione vuoi usare:

NotAbsTotal::work()
{
    AbsInit::work_impl();
}

(AGGIORNATO con sintassi corretta)

Devi iniziare a pensare nei termini di ciò che stai cercando di modellare qui.

L'eredità pubblica dovrebbe essere sempre e solo utilizzata per modellare un "isa" relazione, ad es. un cane è un animale, un quadrato è una forma, ecc.

Dai un'occhiata al libro di Scott Meyer Effective C ++ per un eccellente saggio su come i vari aspetti del design di OO dovrebbero essere interpretati come sempre.

Modifica: ho dimenticato di dire che mentre le risposte finora fornite sono tecnicamente corrette, non penso che nessuno di loro affronti i problemi di ciò che stai cercando di modellare e questo è il punto cruciale del tuo problema!

HTH

applausi,

Rob

Ho trovato un esempio valido e semplice al seguente link. L'articolo spiega con un programma di esempio per il calcolo dell'area e del perimetro di un rettangolo. Puoi verificarlo..cheers

L'ereditarietà multilivello è una gerarchia di ereditarietà in cui una classe derivata eredita da più classi base. Leggi di più ..

http://www.mobihackman.in/2013/09/ multiple-eredità-example.html

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top