2010-09-10 8 views
21

In C++ è possibile utilizzare un'altra classe di base per fornire l'implementazione di un'interfaccia (cioè una classe di base astratta) in una classe derivata?C++: utilizzo di una classe base come implementazione di un'interfaccia

class Base 
{ 
    virtual void myfunction() {/*...*/}; 
} 
class Interface 
{ 
    virtual void myfunction() = 0; 
} 
class Derived 
    : public Base, public Interface 
{ 
    // myfunction is implemented by base 
} 

Quanto sopra deve essere compilato, ma in realtà non funziona. C'è un modo per ottenere questo effetto?

Nel caso in cui qualcuno si preoccupi, la ragione per volere questo è che ha senso (per la mia applicazione) utilizzare una libreria generica da un altro progetto/spazio dei nomi per fornire l'implementazione di un'interfaccia nel mio progetto. Potrei semplicemente avvolgere tutto, ma sembra un sovraccarico in più.

Grazie.

+3

forse se 'base' ereditata da' Interface' ... – djeidot

+0

+1 per una chiara domanda – Chubsdad

+1

@djeidot: '' Interface' e Derived' appartengono alla domanda, 'base' appartiene alla biblioteca. Esistono molti casi in cui si desidera non avere interfacce condivise con una libreria, riutilizzare solo l'implementazione per supportare le interfacce specifiche dell'applicazione. Funzionalità di ObjC (e numero di altre lingue), ma non funziona in C++. – Dummy00001

risposta

10

Se non è derivato da Interface, sarà necessario inoltrare le chiamate in Derived. È solo "sovraccarico" nel senso che devi scrivere codice extra. Sospetto che l'ottimizzatore lo renderà efficiente come se la tua idea originale avesse funzionato.

class Interface { 
    public: 
     virtual void myfunction() = 0; 
}; 

class Base { 
    public: 
     virtual void myfunction() {/*...*/} 
}; 

class Derived : public Interface, public Base { 
    public: 
     void myfunction() { Base::myfunction(); } // forwarding call 
}; 

int main() { 
    Derived d; 
    d.myfunction(); 
    return 0; 
} 
+0

Questo è quello che stavo supponendo che avrei dovuto finire. Devo inoltrarlo esplicitamente o esiste un modo per ottenere la stessa cosa con una direttiva using? (ad esempio usando Base :: miafunzione();). Funzionerebbe? – deuberger

+0

Qualcuno sa per certo se l'ottimizzatore lo renderebbe altrettanto efficiente? Sto usando gcc 4.5.1 su fedora 13. – deuberger

8

Prova questo:

class Interface 
{ 
    virtual void myfunction() = 0; 
} 
class Base : public Interface 
{ 
    virtual void myfunction() {/*...*/}; 
} 
class Derived 
    : public Base 
{ 
    // myfunction is implemented by base 
} 
+4

Naturalmente, questo è il modo preferito. Tuttavia, OP dice che vuole "usare una biblioteca generica da un altro progetto/spazio dei nomi per fornire l'implementazione di un'interfaccia nel mio progetto". Quindi non penso che possa derivare Base da Interface. – Sjoerd

+1

Potrei farlo poiché controllo entrambe le librerie e ci ho pensato, ma per me non ha senso da un punto di vista del design. L'altra libreria non ha motivo di implementare questa interfaccia e penso che potrebbe confondere gli altri utenti della libreria. – deuberger

1

La risposta è supponendo che la classe derivata vuole essere una classe CONCRETE o una classe non astratta, cioè si desidera poter istanziare oggetti di tipo 'Derived'.. Assumo anche funzioni pubbliche per i membri nella mia risposta.

No. La classe derivata deve implementare tutte le funzioni virtuali pure che eredita da tutte le classi base. In questo caso 'Base::myfunction' se ereditato da 'Derived' non è trattato come un'implementazione di 'Derived::myfunction'

'Derived::myfunction' deve ancora fornire un'implementazione 'Interface::myfunction'.

Una possibilità potrebbe essere che 'Derived::myfunction' può delegare internamente 'Base::myfunction'

Tuttavia se non si desidera che Derived sia una classe concreta (che dubito sia l'intento di OP), quindi la suddetta disposizione delle classi va bene (dal punto di vista linguistico)

1

Base e interfaccia sono tipi completamente diversi. In che modo il compilatore dovrebbe sapere che la "miafunzione" è correlata? È necessario implementarlo in Derivato, anche se tale implementazione chiama semplicemente la versione Base.

6

No. (non in quel modo in ogni caso)

Si potrebbe trarre in inganno dal modo in cui le cose vengono fatte in altri linguaggi come Java, C#, ActionScript, ecc

In C++, l'ereditarietà multipla e il modo le classi virtuali sono gestite rende obsolete le interfacce (utilizzate in altri linguaggi). In questi altri linguaggi, le interfacce vengono utilizzate per risolvere i problemi emessi dalla mancanza di ereditarietà multipla (buona o cattiva, è una scelta).

Quindi, se ciò che si vuole fare è fornire un'interfaccia generale, con alcuni metodi virtuali che forniscono implementazioni di default, basta implementare nella classe base:

class Interface 
{ 
    virtual void myfunction() { /*...*/ } ; //default implementation 
    virtual void yourFunction() = 0 ; // this one HAVE TO be implemented by the user 
} 
class Derived 
    : public public Interface // dont' need another clas 
{ 
    // myfunction is implemented by base 
    void yourFunction(); // have to implement yourFunction 
} 
class DerivedB 
    : public public Interface // dont' need another clas 
{ 
    void myFunction();// myfunction is implemented by base but we implement it for this specific class 
    void yourFunction(); // have to implement yourFunction 
} 

Se, tuttavia, si desidera fornire diverse classi di base che hanno le stesse interfacce, quindi pensare che la classe interfaccia è alla base delle altre classi

// in this order 
class Interface 
{ 
    virtual void myfunction() = 0; 
} 
class BaseA : public Interface 
{ 
    // here "virtual" is optional as if the parent is virtual, the child is virtual too 
    virtual void myfunction() {/*...*/}; // BaseA specific implementation 
} 
class BaseB : public Interface 
{ 
    virtual void myfunction() {/*...*/}; // BaseB specific implementation 
} 

C'è comunque una non-realtà-easy-to-read (leggi: non recommanded) modo per fornire un'implementazione di default MA costringendo l'utente a e Diciamo esplicitamente se vuole usarlo o no. Essa sfrutta il fatto che le funzioni virtuali, anche puri possono avere implementazioni predefinite che possono essere chiamati:

class Interface 
{ 
    virtual void myfunction() { /*...*/ } ; //default implementation 
    virtual void yourFunction() = 0 ; // this one HAVE TO be implemented by the user BUT provide a default implementation! 
} 

// in Interface.cpp 

void Interface::yourFunction() // default implementation of the virtual pure function 
{ /*...*/ } 

// in Derived.h 

class DerivedA 
    : public public Interface // dont' need another clas 
{ 
    // myfunction is implemented by base 
    void yourFunction(); // have to implement yourFunction -- DerivedA specific 
} 

class DerivedB 
    : public public Interface // dont' need another clas 
{ 
    void myFunction();// myfunction is implemented by base but we implement it for this specific class 
    void yourFunction() { Interface::yourFunction(); } // uses default implementation of yourFunction, hidden but existing 
} 

Ma non farlo.

+0

+1 per aver menzionato che C++ e Java/C# differiscono su questo punto. – Sjoerd

+0

Hai ragione, inizialmente avevo l'idea basata sulla mia esperienza in Java e C#, ma poi ho realizzato che la stessa cosa non funzionava in C++. Non sono sicuro se penso che sia buono o cattivo, sarebbe sicuramente più facile se funzionasse, ma so che c'è una buona ragione che non lo è. – deuberger

+0

Hai più scelta rispetto a quelle delle lingue precedenti, ma questo ti dà anche più conseguenze. Sarei te, l'unica domanda sarebbe: fornirò molteplici implementazioni predefinite? Se no, vai con la prima soluzione semplice (basta definire l'implementazione predefinita in funzioni virtuali non pure); in caso contrario, è necessaria una "gerarchia di concetti", rappresentata dalla propria gerarchia di classi come secondo esempio. – Klaim

Problemi correlati