Cas d’utilisation de fonctions virtuelles pures avec le corps?

J’ai récemment appris que dans C ++, les fonctions virtuelles pures peuvent éventuellement avoir un corps.

Quels sont les cas d’utilisation réels pour de telles fonctions?

Le classique est un destructeur virtuel pur:

class abstract { public: virtual ~abstract() = 0; }; abstract::~abstract() {} 

Vous le rendez pur car il n’y a rien d’autre à faire, et vous voulez que la classe soit abstraite, mais vous devez néanmoins fournir une implémentation, car les destructeurs des classes dérivées vous appellent explicitement. Oui, je sais, un exemple de manuel assez idiot, mais en tant que tel, c’est un classique. Cela doit avoir été dans la première édition du langage de programmation C ++ .

Quoi qu’il en soit, je ne me souviens plus jamais avoir vraiment besoin de pouvoir implémenter une fonction virtuelle pure. Pour moi, il semble que la seule raison pour laquelle cette fonctionnalité existe, c’est qu’elle aurait dû être explicitement interdite et que Stroustrup n’en voyait aucune raison.

Si jamais vous sentez que vous avez besoin de cette fonctionnalité, vous êtes probablement sur la mauvaise voie avec votre conception.

Les fonctions virtuelles pures avec ou sans corps signifient simplement que les types dérivés doivent fournir leur propre implémentation.

Les corps de fonction virtuels purs dans la classe de base sont utiles si vos classes dérivées veulent appeler votre implémentation de classe de base.

Une des raisons pour lesquelles une classe de base abstraite (avec une fonction virtuelle pure) pourrait fournir une implémentation pour une fonction virtuelle pure qu’elle déclare être de laisser les classes dérivées avoir une «valeur par défaut» facile à utiliser. Il n’y a pas beaucoup d’avantages à cela par rapport à une fonction virtuelle normale qui peut être éventuellement remplacée – en fait, la seule différence réelle est que vous forcez la classe dérivée à être explicite sur l’implémentation de la classe de base ‘default’ :

 class foo { public: virtual int interface(); }; int foo::interface() { printf( "default foo::interface() called\n"); return 0; }; class pure_foo { public: virtual int interface() = 0; }; int pure_foo::interface() { printf( "default pure_foo::interface() called\n"); return 42; } //------------------------------------ class foobar : public foo { // no need to override to get default behavior }; class foobar2 : public pure_foo { public: // need to be explicit about the override, even to get default behavior virtual int interface(); }; int foobar2::interface() { // foobar is lazy; it'll just use pure_foo's default return pure_foo::interface(); } 

Je ne suis pas sûr qu’il y ait beaucoup d’avantages – peut-être que dans les cas où un design a débuté avec une classe abstraite, alors avec le temps, beaucoup de classes concrètes dérivées ont implémenté le même comportement. dans une implémentation de classe de base pour la fonction virtuelle pure.

Je suppose qu’il pourrait également être raisonnable de mettre un comportement commun dans l’implémentation de la classe de base de la fonction virtuelle pure à laquelle on pourrait s’attendre à ce que les classes dérivées modifient / améliorent / augmentent.

Un cas d’utilisation appelle la fonction virtuelle pure à partir du constructeur ou du destructeur de la classe.

Le tout-puissant Herb Sutter, ancien président du comité standard C ++, a donné trois scénarios dans lesquels vous pourriez envisager d’implémenter des méthodes virtuelles pures.

Je dois le dire personnellement – je ne trouve aucune d ‘entre elles convaincante et considère généralement que c’est l’ une des verrues sémantiques de C ++. Il semble que C ++ fait tout son possible pour créer et déchiffrer les vtables abstraites-parentales, mais ne les expose que brièvement pendant la construction / destruction de l’enfant, et les experts de la communauté recommandent à l’unanimité de ne jamais les utiliser .

La seule différence de la fonction virtuelle avec le corps et la fonction virtuelle pure avec le corps est que l’existence de la seconde empêche l’instanciation. Vous ne pouvez pas marquer de résumé de classe en c ++.

Cette question peut vraiment être source de confusion en apprenant OOD et C ++. Personnellement, une chose qui me revenait constamment dans la tête était: Si j’avais besoin d’une fonction Pure Virtual pour avoir aussi une implémentation, alors pourquoi la rendre “Pure” en première place? Pourquoi ne pas le laisser seulement “virtuel” et en tirer à la fois des avantages et remplacer l’implémentation de base?

La confusion vient du fait que de nombreux développeurs considèrent le non corps / implémentation comme le principal objective / avantage de définir une fonction virtuelle pure. Ce n’est pas vrai! L’absence de corps est dans la plupart des cas une conséquence logique de la présence d’une fonction virtuelle pure. L’avantage principal d’avoir une fonction virtuelle pure est de définir un contrat! En définissant une fonction virtuelle pure, vous voulez forcer chaque dérivé à toujours fournir leur propre implémentation de la fonction. Cet aspect “CONTRACT” est très important, surtout si vous développez quelque chose comme une API publique. Rendre la fonction uniquement virtuelle n’est pas suffisant car les produits dérivés ne sont plus obligés de fournir leur propre implémentation, vous pouvez donc perdre l’aspect contractuel (cela peut être limitatif dans le cas d’une API publique). Comme on le dit couramment: “Les fonctions virtuelles PEUVENT être surpassées, les fonctions Pure Virtual DOIVENT être neutralisées.” Et dans la plupart des cas, les contrats sont des concepts abstraits, de sorte qu’il n’est pas logique que les fonctions virtuelles pures correspondantes aient une implémentation.

Mais parfois, et parce que la vie est étrange, vous voudrez peut-être établir un contrat solide entre les produits dérivés et également souhaiter bénéficier d’une implémentation par défaut tout en spécifiant leur propre comportement pour le contrat. Même si la plupart des auteurs de livres recommandent d’éviter de se retrouver dans ces situations, le langage nécessaire pour fournir un filet de sécurité pour prévenir le pire! Une fonction virtuelle simple ne suffirait pas car il pourrait y avoir un risque d’échapper au contrat. La solution C ++ fournie devait donc permettre aux fonctions virtuelles pures de fournir également une implémentation par défaut.

L’article de Sutter cité ci-dessus donne des exemples d’utilisation intéressants de fonctions de Virtual Pure avec le corps.