218 votes

Utilisation de "super" en C++

Mon style de codage comprend l'idiome suivant :

class Derived : public Base
{
   public :
      typedef Base super; // note that it could be hidden in
                          // protected/private section, instead

      // Etc.
} ;

Cela me permet d'utiliser "super" comme alias de Base, par exemple, dans les constructeurs :

Derived(int i, int j)
   : super(i), J(j)
{
}

Ou même en appelant la méthode depuis la classe de base dans sa version surchargée :

void Derived::foo()
{
   super::foo() ;

   // ... And then, do something else
}

Il peut même être enchaîné (je n'en ai pas encore trouvé l'utilité, cependant) :

class DerivedDerived : public Derived
{
   public :
      typedef Derived super; // note that it could be hidden in
                             // protected/private section, instead

      // Etc.
} ;

void DerivedDerived::bar()
{
   super::bar() ; // will call Derived::bar
   super::super::bar ; // will call Base::bar

   // ... And then, do something else
}

Quoi qu'il en soit, je trouve l'utilisation de "typedef super" très utile, par exemple, lorsque Base est soit verbeux et/ou modélisé.

Le fait est que super est implémenté en Java, ainsi qu'en C# (où il est appelé "base", sauf erreur de ma part). Mais le C++ ne dispose pas de ce mot-clé.

Donc, mes questions :

  • Cette utilisation de typedef est-elle super commune/rare/jamais vue dans le code avec lequel vous travaillez ?
  • cette utilisation du typedef est-elle super Ok (c'est-à-dire que vous voyez des raisons fortes ou moins fortes de ne pas l'utiliser) ?
  • est-ce que "super" est une bonne chose, est-ce qu'il devrait être quelque peu standardisé en C++, ou est-ce que cette utilisation à travers un typedef est déjà suffisante ?

Edita: Roddy a mentionné le fait que le typedef devrait être privé. Cela signifierait que toute classe dérivée ne pourrait pas l'utiliser sans le redéclarer. Mais je suppose que cela empêcherait aussi l'enchaînement super::super (mais qui va pleurer pour ça ?).

Edit 2 : Aujourd'hui, quelques mois après avoir utilisé massivement "super", je suis tout à fait d'accord avec le point de vue de Roddy : "super" devrait être privé. Je voudrais upvoter sa réponse deux fois, mais je suppose que je ne peux pas.

0 votes

Génial ! Exactement ce que je cherchais. Je ne pense pas avoir eu besoin d'utiliser cette technique jusqu'à présent. Excellente solution pour mon code multiplateforme.

7 votes

Pour moi super ressemble à Java et il n'y a rien de mauvais, mais... Mais C++ prend en charge l'héritage multiple.

2 votes

@user2623967 : Exact. Dans le cas d'un héritage simple, un "super" est suffisant. Maintenant, si vous avez un héritage multiple, avoir "superA", "superB", etc. est une bonne solution : Vous VOULEZ appeler la méthode à partir d'une implémentation ou d'une autre, donc vous devez DIRE quelle implémentation vous voulez. L'utilisation de typedef de type "super" vous permet de fournir un nom facilement accessible/inscriptible (au lieu, par exemple, d'écrire MyFirstBase<MyString, MyStruct<MyData, MyValue>> partout)

168voto

Max Lybbert Points 11822

Mentions de Bjarne Stroustrup en Conception et évolution du C++ que super comme mot-clé a été envisagé par le comité des normes ISO C++ lors de la première normalisation du C++.

Dag Bruck a proposé cette extension, en appelant la classe de base "héritée". La proposition mentionnait la question de l'héritage multiple, et aurait signalé les utilisations ambiguës. Même Stroustrup était convaincu.

Après discussion, Dag Bruck (oui, la même personne que celle qui a fait la proposition) a écrit que la proposition était implémentable, techniquement saine et sans défauts majeurs, et qu'elle gérait l'héritage multiple. D'un autre côté, il n'y avait pas assez d'avantages et le comité devrait s'occuper d'un problème plus épineux.

Michael Tiemann est arrivé en retard, et a ensuite montré qu'un super typedef'ed fonctionnerait très bien, en utilisant la même technique que celle demandée dans ce post.

Donc, non, cela ne sera probablement jamais normalisé.

Si vous n'avez pas de copie, Conception et évolution vaut bien le prix d'entrée. On peut se procurer des exemplaires d'occasion pour environ 10 $.

7 votes

D & E est en effet un bon livre. Mais il semble que je doive le relire - je ne me souviens d'aucune des deux histoires.

2 votes

Je me souviens de trois caractéristiques qui n'étaient pas acceptées et discutées dans D&E. Il s'agit de la première (cherchez "Michael Tiemann" dans l'index pour trouver l'article), la règle des deux semaines est la deuxième (cherchez "règle des deux semaines" dans l'index), et la troisième était les paramètres nommés (cherchez "arguments nommés" dans l'index).

12 votes

Il y a un défaut majeur dans le typedef technique : il ne respecte pas le DRY. Le seul moyen de contourner ce problème serait d'utiliser d'affreuses macros pour déclarer les classes. Lorsque vous héritez, la base pourrait être une longue classe template multi paramètres, ou pire. (par exemple, les classes multiples), vous devriez réécrire tout cela une deuxième fois. Enfin, je vois un gros problème avec les bases de modèles qui ont des arguments de classe de modèle. Dans ce cas, le super est un template (et non une instanciation d'un template). Ce qui ne peut pas être typographié. Même en C++11, il faut using pour ce cas.

111voto

Roddy Points 32503

J'ai toujours utilisé "inherited" plutôt que super. (Probablement en raison de mes antécédents avec Delphi), et je fais toujours en sorte que privé Cette méthode permet d'éviter le problème lorsque le mot "hérité" est omis par erreur dans une classe et qu'une sous-classe tente de l'utiliser.

class MyClass : public MyBase
{
private:  // Prevents erroneous use by other classes.
  typedef MyBase inherited;
...

Mon "modèle de code" standard pour la création de nouvelles classes inclut le typedef, et j'ai donc peu d'occasions de l'omettre accidentellement.

Je ne pense pas que la suggestion d'enchaîner les "super::super" soit une bonne idée. Si vous faites cela, vous êtes probablement très attaché à une hiérarchie particulière, et la modifier risque de casser gravement des choses.

2 votes

Quant à l'enchaînement de super::super, comme je l'ai mentionné dans la question, je n'ai pas encore trouvé d'utilisation intéressante à cela. Pour l'instant, je ne le vois que comme un hack, mais cela valait la peine d'être mentionné, ne serait-ce que pour les différences avec Java (où l'on ne peut pas enchaîner "super").

5 votes

Après quelques mois, je me suis converti à votre point de vue (et j'ai bien eu un bug à cause d'un "super" oublié, comme vous l'avez mentionné...). Tu as tout à fait raison dans ta réponse, y compris pour l'enchaînement, je suppose. ^_^ ...

0 votes

Comment puis-je l'utiliser maintenant pour appeler les méthodes de la classe mère ?

39voto

Kristopher Johnson Points 34554

Le problème est que si vous oubliez de (re)définir super pour les classes dérivées, alors tout appel à super::quelque chose compilera bien mais n'appellera probablement pas la fonction désirée.

Par exemple :

class Base
{
public:  virtual void foo() { ... }
};

class Derived: public Base
{
public:
    typedef Base super;
    virtual void foo()
    {
        super::foo();   // call superclass implementation

        // do other stuff
        ...
    }
};

class DerivedAgain: public Derived
{
public:
    virtual void foo()
    {
        // Call superclass function
        super::foo();    // oops, calls Base::foo() rather than Derived::foo()

        ...
    }
};

(Comme l'a souligné Martin York dans les commentaires de cette réponse, ce problème peut être éliminé en rendant le typedef privé plutôt que public ou protégé).

0 votes

Merci pour la remarque. Cet effet secondaire m'avait échappé. Alors qu'il ne compilera probablement pas pour l'utilisation du constructeur, partout ailleurs, je suppose que le bug sera là.

6 votes

Cependant, le typedef privé empêchera l'utilisation en chaîne, mentionnée dans le post original.

1 votes

C'est ce bogue qui m'a amené à poser cette question :(

21voto

krujos Points 147

FWIW Microsoft a ajouté une extension pour __super dans leur compilateur.

0 votes

Quelques développeurs ici ont commencé à faire pression pour utiliser __super. Au début, j'ai refusé, car je trouvais que c'était "incorrect" et "non standard". CEPENDANT, j'ai appris à l'aimer.

10 votes

Je travaille sur une application Windows et j'adore l'extension __super. Cela m'attriste que le comité de normalisation l'ait rejetée en faveur de l'astuce typedef mentionnée ici, car bien que cette astuce typedef soit bonne, elle nécessite plus de maintenance qu'un mot-clé du compilateur lorsque vous changez la hiérarchie d'héritage, et gère correctement l'héritage multiple (sans nécessiter deux typedefs comme super1 et super2). En bref, je suis d'accord avec l'autre commentateur pour dire que l'extension MS est TRÈS utile, et que toute personne utilisant exclusivement Visual Studio devrait fortement envisager de l'utiliser.

15voto

Colin Jensen Points 1555

Super (ou hérité) est une très bonne chose car si vous avez besoin de coller une autre couche d'héritage entre Base et Dérivé, vous ne devez changer que deux choses : 1. la "classe Base : foo" et 2. le typedef

Si je me souviens bien, le comité des normes C++ envisageait d'ajouter un mot-clé pour cela... jusqu'à ce que Michael Tiemann fasse remarquer que cette astuce de typedef fonctionne.

En ce qui concerne l'héritage multiple, puisque c'est sous le contrôle du programmeur, vous pouvez faire ce que vous voulez : peut-être super1 et super2, ou autre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X