2010-09-10 5 views
21

En C++, est-il possible d'utiliser une autre classe de base pour fournir l'implémentation d'une interface (classe de base abstraite) dans une classe dérivée?C++: utilisation d'une classe de base comme implémentation d'une interface

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

Ce qui précède doit compiler, mais ne fonctionne pas réellement. Y a-t-il un moyen d'atteindre cet effet? Dans le cas où quelqu'un s'en soucie, la raison en est que cela a du sens (pour mon application) d'utiliser une bibliothèque générique d'un autre projet/espace de noms pour fournir l'implémentation d'une interface dans mon projet. Je pourrais juste tout emballer, mais cela semble beaucoup de frais généraux supplémentaires.

Merci.

+3

peut-être si '' base' hérité de Interface' ... – djeidot

+0

+1 pour la question claire – Chubsdad

+1

@djeidot: '' Interface' et Derived' appartiennent à l'application, 'base' appartient à la bibliothèque. Il existe de nombreux cas où l'on souhaite ne pas avoir d'interfaces partagées avec une bibliothèque - ne réutilisez que l'implémentation pour prendre en charge les interfaces spécifiques à l'application. Caractéristique de ObjC (et le nombre d'autres langues), mais ne fonctionne pas en C++. – Dummy00001

Répondre

10

Si Base n'est pas dérivé de Interface, vous devrez alors transférer les appels au Derived. C'est seulement "overhead" dans le sens où vous devez écrire du code supplémentaire. Je soupçonne l'optimiseur de le rendre aussi efficace que si votre idée originale avait fonctionné.

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

C'est ce que je devais deviner. Dois-je le transmettre explicitement ou y a-t-il un moyen d'obtenir la même chose avec une directive d'utilisation? (par exemple en utilisant Base :: myfunction();). Cela fonctionnerait-il? – deuberger

+0

Est-ce que quelqu'un sait avec certitude si l'optimiseur rendrait cela aussi efficace? J'utilise gcc 4.5.1 sur fedora 13. – deuberger

8

Essayez ceci:

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

Bien sûr, c'est le moyen préféré. Cependant, OP dit qu'il veut "utiliser une bibliothèque générique d'un autre projet/espace de noms pour fournir l'implémentation d'une interface dans mon projet." Donc je ne pense pas qu'il puisse dériver Base from Interface. – Sjoerd

+1

Je pourrais le faire puisque je contrôle les deux bibliothèques et j'y ai réfléchi, mais cela n'a pas vraiment de sens pour moi du point de vue du design. L'autre bibliothèque n'a aucune raison d'implémenter cette interface et je pense que cela perturberait d'autres utilisateurs de la bibliothèque. – deuberger

1

La réponse est en supposant que la classe dérivée veut être une CONCRETE classe ou une classe non abstraite, i.e. on souhaite pouvoir instancier des objets de type 'Derived'.. Aussi, j'assume des fonctions de membre public dans ma réponse. Non. La classe dérivée doit implémenter toutes les fonctions virtuelles pures dont elle hérite de toutes les classes de base. Dans ce cas 'Base::myfunction' bien hérité par 'Derived' ne soit pas considérée comme une mise en œuvre de 'Derived::myfunction'

'Derived::myfunction' doit encore fournir une implémentation de 'Interface::myfunction'.

Une possibilité peut être que 'Derived::myfunction' interne peut déléguer à 'Base::myfunction'

Toutefois, si il n'est pas souhaité que Derived soit une classe concrète (ce que je doute est l'intention de OP), alors l'arrangement des classes ci-dessus est bon (du point de vue du langage)

1

La base et l'interface sont des types totalement différents. Comment le compilateur est censé savoir que "myfunction" est lié? Vous devez l'implémenter dans Derived, même si cette implémentation appelle simplement la version de base.

6

Non (pas de cette façon de toute façon)

Vous pourriez être induits en erreur par la façon dont les choses sont faites dans d'autres langages comme Java, C#, ActionScript, etc.

En C++, l'héritage multiple et la manière les classes virtuelles sont gérées et rend obsolètes les interfaces (utilisées dans d'autres langues). Dans ces autres langages, les interfaces sont utilisées pour corriger les problèmes issus du manque d'héritage multiple (bon ou mauvais, c'est un choix).

Donc, si ce que vous voulez faire est de fournir juste une interface générale avec des méthodes virtuelles fournissant des implémentations par défaut, tout mettre en œuvre dans la classe de 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 
} 

Cependant, si vous voulez fournir plusieurs classes de base qui ont les mêmes interfaces, alors pensez que votre classe d'interface est la base des autres classes

// 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 
} 

Il y a cependant un pas-vraiment-facile à lire (lire: non recommandé) moyen de fournir une implémentation par défaut MAIS forcer l'utilisateur à e xplicitement dire s'il veut l'utiliser ou non. Il exploite le fait que même des fonctions virtuelles pures peuvent avoir des implémentations par défaut qui peuvent être appelées:

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 
} 

Mais ne le font pas.

+0

+1 pour mentionner que C++ et Java/C# diffèrent sur ce point. – Sjoerd

+0

Vous avez raison, j'avais initialement l'idée basée sur mon expérience en Java et en C#, mais j'ai réalisé que la même chose ne marcherait pas en C++. Je ne suis pas sûr si je pense que c'est bon ou mauvais encore, ce serait certainement plus facile si cela fonctionnait, mais je sais qu'il y a une bonne raison que ce n'est pas le cas. – deuberger

+0

Vous avez plus de choix que dans ces langues précédentes, mais cela vous donne aussi plus de conséquences. Je serais vous, la seule question serait: vais-je fournir plusieurs implémentations par défaut? Si non, passez à la première solution simple (Définissez simplement l'implémentation par défaut dans les fonctions virtuelles non pures); Sinon, vous avez besoin d'une «hiérarchie conceptuelle», représentée par votre hiérarchie de classes en tant que second exemple. – Klaim

Questions connexes