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 lors de l'appel de la méthode à partir de la classe de base dans sa version remplacée:
void Derived::foo()
{
super::foo() ;
// ... And then, do something else
}
Il peut même être enchaîné (je dois encore trouver l'utilisation pour cela):
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 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). Mais C ++ n'a pas ce mot-clé.
Alors, mes questions:
- cette utilisation de typedef est-elle super courante / rare / jamais vue dans le code avec lequel vous travaillez?
- cette utilisation de typedef est-elle super ok (c.-à-d. voyez-vous des raisons fortes ou moins fortes de ne pas l'utiliser)?
- "super" devrait-il être une bonne chose, devrait-il être quelque peu standardisé en C ++, ou est-ce déjà une utilisation via un typedef?
Edit: Roddy a mentionné le fait que le typedef devrait être privé. Cela signifierait qu'aucune classe dérivée ne pourrait l'utiliser sans la redéclarer. Mais je suppose que cela empêcherait également le super :: super chaînage (mais qui va pleurer pour ça?).
Edit 2: Maintenant, quelques mois après avoir utilisé massivement "super", je suis entièrement d'accord avec le point de vue de Roddy: "super" devrait être privé. Je voterais deux fois sa réponse, mais je suppose que je ne peux pas.
la source
super
ressembleJava
et ce n'est rien de mal, mais ... MaisC++
supporte l'héritage multiple.MyFirstBase<MyString, MyStruct<MyData, MyValue>>
partout)template <class baz> struct Foo {...void bar() {...} ...}; struct Foo2: Foo<AnnoyinglyLongListOfArguments> { void bar2() { ... Foo::bar(); ...} };
Cela m'a fonctionné avec gcc 9.1 --std = c ++ 1y (c ++ 14).Réponses:
Bjarne Stroustrup mentionne dans Design and Evolution of C ++ que
super
comme mot-clé a été considéré par le comité des normes ISO C ++ la première fois que C ++ a été standardisé.Dag Bruck a proposé cette extension, appelant la classe de base "héritée". La proposition mentionnait la question de l'héritage multiple et aurait signalé des utilisations ambiguës. Même Stroustrup était convaincu.
Après discussion, Dag Bruck (oui, la même personne qui a fait la proposition) a écrit que la proposition était implémentable, techniquement solide et exempte de défauts majeurs, et traitait l'héritage multiple. D'un autre côté, le rapport qualité-prix n'était pas suffisant et le comité devrait gérer un problème plus épineux.
Michael Tiemann est arrivé en retard, puis a montré qu'un super typé fonctionnerait très bien, en utilisant la même technique que celle décrite dans ce post.
Donc, non, cela ne sera probablement jamais standardisé.
Si vous n'en avez pas, Design and Evolution vaut bien le prix de couverture. Les copies utilisées peuvent être obtenues pour environ 10 $.
la source
typedef
technique: elle ne respecte pas DRY. La seule solution serait d'utiliser des macros laides pour déclarer des classes. Lorsque vous héritez, la base peut être une longue classe de modèles multi-paramètres, ou pire. (par exemple multi classes), vous devrez 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èles. Dans ce cas, le super est un modèle (et non l'instanciation d'un modèle). Qui ne peut pas être tapé. Même en C ++ 11, vous avez besoinusing
de ce cas.J'ai toujours utilisé "hérité" plutôt que super. (Probablement en raison d'un arrière-plan Delphi), et je le rends toujours privé , pour éviter le problème lorsque "hérité" est omis par erreur d'une classe mais qu'une sous-classe essaie de l'utiliser.
Mon «modèle de code» standard pour créer de nouvelles classes inclut le typedef, donc j'ai peu d'occasions de l'omettre accidentellement.
Je ne pense pas que la suggestion enchaînée "super :: super" soit une bonne idée. Si vous faites cela, vous êtes probablement très lié à une hiérarchie particulière, et la modifier entraînera probablement un mauvais fonctionnement.
la source
virtual
comme indiqué ici: martinbroadhurst.com/typedef-super.htmlUn problème avec cela est que si vous oubliez de (re) définir super pour les classes dérivées, alors tout appel à super :: quelque chose se compilera correctement mais n'appellera probablement pas la fonction souhaitée.
Par exemple:
(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é.)
la source
super1
pour Base etsuper2
dans Derived? Le DerivedAgain peut utiliser les deux?FWIW Microsoft a ajouté une extension pour __super dans leur compilateur.
la source
Super (ou hérité) est une très bonne chose parce que si vous devez coller une autre couche d'héritage entre Base et Derived, vous n'avez qu'à changer 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 souligne que cette astuce typedef fonctionne.
En ce qui concerne l'héritage multiple, car il est sous le contrôle du programmeur, vous pouvez faire tout ce que vous voulez: peut-être super1 et super2, ou autre chose.
la source
Je viens de trouver une solution alternative. J'ai un gros problème avec l'approche typedef qui m'a mordu aujourd'hui:
J'ai donc trouvé une meilleure solution en utilisant un modèle très simple.
Alors maintenant, au lieu de
vous avez
Dans ce cas,
BaseAlias
n'est pas privé et j'ai essayé de me prémunir contre une utilisation imprudente en sélectionnant un nom de type qui devrait alerter les autres développeurs.la source
public
alias est un inconvénient, car vous êtes ouvert au bug mentionné dans les réponses de Roddy et Kristopher (par exemple, vous pouvez (par erreur) dériver à laDerived
place deMakeAlias<Derived>
)MakeAlias
ou une transmission parfaite, mais cela vous oblige à vous référer auxMakeAlias<BaseAlias>
constructeurs plutôt qu'à vous référerC
directement aux constructeurs de.)Je ne me souviens pas avoir vu cela auparavant, mais à première vue, j'aime ça. Comme le note Ferruccio , cela ne fonctionne pas bien face à l'IM, mais l'IM est plus l'exception que la règle et rien ne dit que quelque chose doit être utilisable partout pour être utile.
la source
J'ai vu cet idiome utilisé dans de nombreux codes et je suis presque sûr de l'avoir même vu quelque part dans les bibliothèques de Boost. Cependant, pour autant que je m'en souvienne, le nom le plus courant est
base
(ouBase
) au lieu desuper
.Cet idiome est particulièrement utile si vous travaillez avec des classes de modèles. À titre d'exemple, considérons la classe suivante (à partir d'un vrai projet ):
Ne vous occupez pas des noms drôles. Le point important ici est que la chaîne d'héritage utilise des arguments de type pour obtenir un polymorphisme au moment de la compilation. Malheureusement, le niveau d'imbrication de ces modèles devient assez élevé. Par conséquent, les abréviations sont cruciales pour la lisibilité et la maintenabilité.
la source
Je l'ai assez souvent vu utilisé, parfois en tant que super_t, lorsque la base est un type de modèle complexe (
boost::iterator_adaptor
fait cela, par exemple)la source
cette utilisation de typedef est-elle super courante / rare / jamais vue dans le code avec lequel vous travaillez?
Je n'ai jamais vu ce modèle particulier dans le code C ++ avec lequel je travaille, mais cela ne signifie pas qu'il n'est pas là.
cette utilisation de typedef est-elle super ok (c.-à-d. voyez-vous des raisons fortes ou moins fortes de ne pas l'utiliser)?
Il ne permet pas l'héritage multiple (proprement, de toute façon).
"super" devrait-il être une bonne chose, devrait-il être quelque peu standardisé en C ++, ou est-ce déjà une utilisation via un typedef?
Pour la raison citée ci-dessus (héritage multiple), non. La raison pour laquelle vous voyez "super" dans les autres langues que vous avez répertoriées est qu'elles ne prennent en charge que l'héritage unique, donc il n'y a aucune confusion quant à ce à quoi "super" fait référence. Certes, dans ces langages, il est utile, mais il n'a pas vraiment sa place dans le modèle de données C ++.
Oh, et pour info: C ++ / CLI prend en charge ce concept sous la forme du mot clé "__super". Veuillez noter, cependant, que C ++ / CLI ne prend pas en charge l'héritage multiple non plus.
la source
Une raison supplémentaire d'utiliser un typedef pour la superclasse est lorsque vous utilisez des modèles complexes dans l'héritage de l'objet.
Par exemple:
Dans la classe B, il serait idéal d'avoir un typedef pour A, sinon vous seriez obligé de le répéter partout où vous voulez référencer les membres de A.
Dans ces cas, il peut également fonctionner avec plusieurs héritages, mais vous n'auriez pas de typedef nommé 'super', il serait appelé 'base_A_t' ou quelque chose comme ça.
--jeffk ++
la source
Après avoir migré de Turbo Pascal vers C ++ dans la journée, je faisais cela pour avoir un équivalent pour le mot-clé "hérité" de Turbo Pascal, qui fonctionne de la même manière. Cependant, après avoir programmé en C ++ pendant quelques années, j'ai arrêté de le faire. J'ai trouvé que je n'en avais pas vraiment besoin.
la source
Je ne sais pas si c'est rare ou pas, mais j'ai certainement fait la même chose.
Comme cela a été souligné, la difficulté de faire cette partie du langage lui-même est quand une classe utilise l'héritage multiple.
la source
J'utilise cela de temps en temps. Juste au moment où je me retrouve à taper le type de classe de base plusieurs fois, je le remplacerai par un typedef similaire au vôtre.
Je pense que cela peut être une bonne utilisation. Comme vous le dites, si votre classe de base est un modèle, elle peut économiser la saisie. De plus, les classes de modèles peuvent prendre des arguments qui agissent comme des politiques sur la façon dont le modèle doit fonctionner. Vous êtes libre de changer le type de base sans avoir à y corriger toutes vos références tant que l'interface de la base reste compatible.
Je pense que l'utilisation via le typedef est déjà suffisante. Je ne vois pas comment il serait intégré au langage de toute façon, car l'héritage multiple signifie qu'il peut y avoir de nombreuses classes de base, vous pouvez donc le taper comme bon vous semble pour la classe que vous pensez logiquement être la classe de base la plus importante.
la source
J'essayais de résoudre exactement ce même problème; J'ai lancé quelques idées, telles que l'utilisation de modèles variadiques et l'expansion du pack pour permettre un nombre arbitraire de parents, mais j'ai réalisé que cela entraînerait une implémentation comme 'super0' et 'super1'. Je l'ai mis à la poubelle parce que ce serait à peine plus utile que de ne pas l'avoir au départ.
Ma solution implique une classe d'assistance
PrimaryParent
et est implémentée comme suit:La classe que vous souhaitez utiliser serait alors déclarée comme telle:
Pour éviter d'avoir à utiliser l'héritage virtuel dans
PrimaryParent
onBaseClass
, un constructeur prenant un nombre variable d'arguments est utilisé pour permettre la construction deBaseClass
.La raison derrière l'
public
héritage deBaseClass
intoPrimaryParent
est de laisserMyObject
avoir un contrôle total sur l'héritage deBaseClass
malgré une classe d'assistance entre eux.Cela signifie que chaque classe que vous souhaitez avoir
super
doit utiliser laPrimaryParent
classe d'assistance, et chaque enfant ne peut hériter que d'une classe utilisantPrimaryParent
(d'où le nom).Une autre restriction pour cette méthode est qu'elle
MyObject
ne peut hériter que d'une classe qui hérite dePrimaryParent
celle-ci et que celle-ci doit être héritée en utilisantPrimaryParent
. Voici ce que je veux dire:Avant de rejeter cela en option en raison du nombre apparent de restrictions et du fait qu'il existe une classe intermédiaire entre chaque héritage, ces choses ne sont pas mauvaises.
L'héritage multiple est un outil puissant, mais dans la plupart des cas, il n'y aura qu'un seul parent principal et s'il y a d'autres parents, ce seront probablement des classes Mixin ou des classes qui n'héritent pas de
PrimaryParent
toute façon. Si l'héritage multiple est toujours nécessaire (bien que de nombreuses situations gagneraient à utiliser la composition pour définir un objet au lieu de l'héritage), définissez simplement explicitementsuper
dans cette classe et n'héritez pas dePrimaryParent
.L'idée de devoir définir
super
dans chaque classe n'est pas très attrayante pour moi, en utilisantPrimaryParent
permetsuper
, clairement un alias basé sur l'héritage, de rester dans la ligne de définition de classe au lieu du corps de classe où les données devraient aller.Mais c'est peut-être juste moi.
Bien sûr, chaque situation est différente, mais tenez compte de ces choses que j'ai dites lors du choix de l'option à utiliser.
la source
Je ne dirai pas grand-chose sauf le code actuel avec des commentaires qui démontre que super ne signifie pas appeler la base!
super != base.
En bref, qu'est-ce que "super" est censé signifier de toute façon? et alors que signifie "base"?
Ces 2 règles s'appliquent aux typedefs en classe.
Considérez l'implémentateur de la bibliothèque et l'utilisateur de la bibliothèque, qui est super et qui est la base?
pour plus d'informations, voici le code de travail pour copier-coller dans votre IDE:
Un autre point important ici est le suivant:
à l'intérieur,
main()
nous utilisons des appels d'appel polymorphes qui super appellent vers le haut, pas vraiment utiles dans la vie réelle, mais cela démontre la différence.la source
J'utilise le mot clé __super. Mais c'est spécifique à Microsoft:
http://msdn.microsoft.com/en-us/library/94dw1w7x.aspx
la source
C'est une méthode que j'utilise qui utilise des macros au lieu d'un typedef. Je sais que ce n'est pas la façon de faire les choses en C ++ mais cela peut être pratique lors de l'enchaînement d'itérateurs par héritage lorsque seule la classe de base la plus en aval de la hiérarchie agit sur un décalage hérité.
Par exemple:
La configuration générique que j'utilise facilite la lecture et le copier / coller entre l'arborescence d'héritage qui a du code en double mais doit être remplacée car le type de retour doit correspondre à la classe actuelle.
On pourrait utiliser des minuscules
super
pour reproduire le comportement vu en Java, mais mon style de codage consiste à utiliser toutes les lettres majuscules pour les macros.la source