Mi estilo de codificación incluye el siguiente modismo:
class Derived : public Base
{
public :
typedef Base super; // note that it could be hidden in
// protected/private section, instead
// Etc.
} ;
Esto me permite usar "super" como un alias de Base, por ejemplo, en constructores:
Derived(int i, int j)
: super(i), J(j)
{
}
O incluso cuando se llama al método desde la clase base dentro de su versión anulada:
void Derived::foo()
{
super::foo() ;
// ... And then, do something else
}
Incluso se puede encadenar (aunque todavía tengo que encontrar el uso para eso):
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
}
De todos modos, considero que el uso de "typedef super" es muy útil, por ejemplo, cuando Base es verbosa o con plantilla.
El hecho es que super se implementa en Java, así como en C # (donde se llama "base", a menos que me equivoque). Pero C ++ carece de esta palabra clave.
Entonces, mis preguntas:
- ¿Es este uso de typedef super común / raro / nunca visto en el código con el que trabaja?
- ¿Es este uso de typedef super Ok (es decir, ves razones fuertes o no tan fuertes para no usarlo)?
- ¿debería ser "super" algo bueno, debería estar algo estandarizado en C ++, o este uso ya no es suficiente?
Editar: Roddy mencionó el hecho de que typedef debería ser privado. Esto significaría que cualquier clase derivada no podría usarla sin redeclararla. Pero supongo que también evitaría el super :: super encadenamiento (pero ¿quién va a llorar por eso?).
Edición 2: Ahora, algunos meses después de usar masivamente "super", estoy totalmente de acuerdo con el punto de vista de Roddy: "super" debería ser privado. Votaría su respuesta dos veces, pero supongo que no puedo.
fuente
super
pareceJava
y no es nada malo, pero ... PeroC++
admite herencia múltiple.MyFirstBase<MyString, MyStruct<MyData, MyValue>>
todas partes)template <class baz> struct Foo {...void bar() {...} ...}; struct Foo2: Foo<AnnoyinglyLongListOfArguments> { void bar2() { ... Foo::bar(); ...} };
Esto me funcionó con gcc 9.1 --std = c ++ 1y (c ++ 14).Respuestas:
Bjarne Stroustrup menciona en Diseño y evolución de C ++ que,
super
como palabra clave, fue considerada por el comité de normas ISO C ++ la primera vez que C ++ se estandarizó.Dag Bruck propuso esta extensión, llamando a la clase base "heredada". La propuesta mencionaba el problema de la herencia múltiple y habría señalado usos ambiguos. Incluso Stroustrup estaba convencido.
Después de la discusión, Dag Bruck (sí, la misma persona que hizo la propuesta) escribió que la propuesta era implementable, técnicamente sólida y libre de fallas importantes, y manejaba la herencia múltiple. Por otro lado, no hubo suficiente inversión para el dinero, y el comité debería manejar un problema más espinoso.
Michael Tiemann llegó tarde, y luego demostró que un superdefinido de tipo funcionaría bien, usando la misma técnica que se le preguntó en esta publicación.
Entonces, no, esto probablemente nunca se estandarizará.
Si no tiene una copia, Design and Evolution bien vale el precio de portada. Se pueden obtener copias usadas por aproximadamente $ 10.
fuente
typedef
técnica: no respeta DRY. La única forma de hacerlo sería usar macros feas para declarar clases. Cuando hereda, la base podría ser una clase de plantilla larga de múltiples parámetros, o peor. (por ejemplo, varias clases), tendría que volver a escribir todo eso por segunda vez. Finalmente, veo un gran problema con las bases de plantillas que tienen argumentos de clase de plantilla. En este caso, el super es una plantilla (y no una instancia de una plantilla). Que no puede ser typedefed. Incluso en C ++ 11 necesitasusing
para este caso.Siempre he usado "heredado" en lugar de super. (Probablemente debido a un fondo de Delphi), y siempre lo hago privado , para evitar el problema cuando el 'heredado' se omite erróneamente de una clase pero una subclase intenta usarlo.
Mi 'plantilla de código' estándar para crear nuevas clases incluye typedef, por lo que tengo pocas oportunidades de omitirla accidentalmente.
No creo que la sugerencia "super :: super" encadenada sea una buena idea. Si lo hace, probablemente esté muy vinculado a una jerarquía particular, y cambiarla probablemente romperá las cosas.
fuente
virtual
como se muestra aquí: martinbroadhurst.com/typedef-super.htmlUn problema con esto es que si olvida (re) definir super para las clases derivadas, cualquier llamada a super :: something se compilará bien pero probablemente no llame a la función deseada.
Por ejemplo:
(Como señaló Martin York en los comentarios a esta respuesta, este problema puede eliminarse haciendo que typedef sea privado en lugar de público o protegido).
fuente
super1
para Base ysuper2
en Derivado? El DerivedAgain puede usar ambos?FWIW Microsoft ha agregado una extensión para __super en su compilador.
fuente
Super (o heredado) es algo muy bueno porque si necesita pegar otra capa de herencia entre Base y Derivada, solo tiene que cambiar dos cosas: 1. la "clase Base: foo" y 2. el typedef
Si no recuerdo mal, el comité de estándares de C ++ estaba considerando agregar una palabra clave para esto ... hasta que Michael Tiemann señaló que este truco de typedef funciona.
En cuanto a la herencia múltiple, ya que está bajo el control del programador, puede hacer lo que quiera: tal vez super1 y super2, o lo que sea.
fuente
Acabo de encontrar una solución alternativa. Tengo un gran problema con el enfoque typedef que me mordió hoy:
Entonces se me ocurrió una solución mejor usando una plantilla muy simple.
Entonces ahora, en lugar de
tienes
En este caso,
BaseAlias
no es privado y he tratado de evitar el uso descuidado seleccionando un nombre de tipo que debería alertar a otros desarrolladores.fuente
public
alias es un inconveniente, ya que estás abierto al error mencionado en las respuestas de Roddy y Kristopher (por ejemplo, puedes (por error) derivar de enDerived
lugar deMakeAlias<Derived>
)MakeAlias
o reenvío perfecto, pero requiere que se refiera a losMakeAlias<BaseAlias>
constructores en lugar de referirseC
directamente a los constructores directamente).No recuerdo haber visto esto antes, pero a primera vista me gusta. Como señala Ferruccio , no funciona bien frente a MI, pero MI es más la excepción que la regla y no hay nada que diga que algo debe ser utilizable en todas partes para ser útil.
fuente
He visto este modismo empleado en muchos códigos y estoy bastante seguro de haberlo visto en algún lugar de las bibliotecas de Boost. Sin embargo, hasta donde recuerdo el nombre más común es
base
(oBase
) en lugar desuper
.Este modismo es especialmente útil si se trabaja con clases de plantilla. Como ejemplo, considere la siguiente clase (de un proyecto real ):
No te preocupes por los nombres graciosos. El punto importante aquí es que la cadena de herencia usa argumentos de tipo para lograr el polimorfismo en tiempo de compilación. Desafortunadamente, el nivel de anidación de estas plantillas es bastante alto. Por lo tanto, las abreviaturas son cruciales para la legibilidad y la mantenibilidad.
fuente
A menudo lo he visto usado, a veces como super_t, cuando la base es un tipo de plantilla compleja (
boost::iterator_adaptor
hace esto, por ejemplo)fuente
¿Es este uso de typedef super común / raro / nunca visto en el código con el que trabaja?
Nunca he visto este patrón particular en el código C ++ con el que trabajo, pero eso no significa que no esté disponible.
¿Es este uso de typedef super Ok (es decir, ves razones fuertes o no tan fuertes para no usarlo)?
No permite la herencia múltiple (limpiamente, de todos modos).
¿debería ser "super" algo bueno, debería estar algo estandarizado en C ++, o este uso ya no es suficiente?
Por la razón antes citada (herencia múltiple), no. La razón por la que ve "super" en los otros idiomas que enumeró es que solo admiten herencia única, por lo que no hay confusión sobre a qué se refiere "super". De acuerdo, en esos lenguajes ES útil, pero en realidad no tiene cabida en el modelo de datos de C ++.
Ah, y para su información: C ++ / CLI admite este concepto en forma de la palabra clave "__super". Sin embargo, tenga en cuenta que C ++ / CLI tampoco admite la herencia múltiple.
fuente
Una razón adicional para usar un typedef para la superclase es cuando usa plantillas complejas en la herencia del objeto.
Por ejemplo:
En la clase B, sería ideal tener un typedef para A, de lo contrario, estaría atascado repitiéndolo en todas partes donde quisiera hacer referencia a los miembros de A.
En estos casos, también puede funcionar con herencia múltiple, pero no tendría un typedef llamado 'super', se llamaría 'base_A_t' o algo así.
--jeffk ++
fuente
Después de migrar de Turbo Pascal a C ++ en el pasado, solía hacer esto para tener un equivalente para la palabra clave "heredada" de Turbo Pascal, que funciona de la misma manera. Sin embargo, después de programar en C ++ durante algunos años, dejé de hacerlo. Descubrí que no lo necesitaba mucho.
fuente
No sé si es raro o no, pero ciertamente hice lo mismo.
Como se ha señalado, la dificultad de hacer que esta parte del lenguaje en sí sea cuando una clase hace uso de la herencia múltiple.
fuente
Lo uso de vez en cuando. Justo cuando me encuentro escribiendo el tipo de clase base un par de veces, lo reemplazaré con un typedef similar al tuyo.
Creo que puede ser un buen uso. Como usted dice, si su clase base es una plantilla, puede guardar la escritura. Además, las clases de plantilla pueden tomar argumentos que actúan como políticas sobre cómo debería funcionar la plantilla. Puede cambiar el tipo de base sin tener que arreglar todas sus referencias a él, siempre y cuando la interfaz de la base siga siendo compatible.
Creo que el uso a través de typedef ya es suficiente. De todos modos, no puedo ver cómo se incorporaría al lenguaje porque la herencia múltiple significa que puede haber muchas clases base, por lo que puede escribirlo como mejor le parezca para la clase que lógicamente considera que es la clase base más importante.
fuente
Estaba tratando de resolver exactamente el mismo problema; Lancé algunas ideas, como el uso de plantillas variadas y la expansión del paquete para permitir un número arbitrario de padres, pero me di cuenta de que eso resultaría en una implementación como 'super0' y 'super1'. Lo boté porque eso sería apenas más útil que no tenerlo para empezar.
Mi solución implica una clase auxiliar
PrimaryParent
y se implementa de la siguiente manera:Entonces, cualquier clase que desee utilizar se declarará como tal:
Para evitar la necesidad de utilizar la herencia virtual en
PrimaryParent
onBaseClass
, se utiliza un constructor que toma un número variable de argumentos para permitir la construcción deBaseClass
.La razón detrás de la
public
herencia deBaseClass
intoPrimaryParent
es permitirMyObject
tener un control total sobre la herencia de aBaseClass
pesar de tener una clase auxiliar entre ellos.Esto significa que cada clase que desee tener
super
debe usar laPrimaryParent
clase auxiliar, y cada hijo solo puede heredar de una clase usandoPrimaryParent
(de ahí el nombre).Otra restricción para este método es
MyObject
que solo se puede heredar una clase de la que se heredaPrimaryParent
, y esa se debe heredar usandoPrimaryParent
. Esto es lo que quiero decir:Antes de descartar esto como una opción debido a la aparente cantidad de restricciones y al hecho de que hay una clase de intermediario entre cada herencia, estas cosas no son malas.
La herencia múltiple es una herramienta sólida, pero en la mayoría de las circunstancias, solo habrá un padre primario, y si hay otros padres, probablemente serán clases Mixin, o clases que no heredan de
PrimaryParent
todos modos. Si aún es necesaria la herencia múltiple (aunque muchas situaciones se beneficiarían al usar la composición para definir un objeto en lugar de la herencia), simplemente defina explícitamentesuper
en esa clase y no herede dePrimaryParent
.La idea de tener que definir
super
en cada clase no es muy atractiva para mí, el usoPrimaryParent
permitesuper
, claramente un alias basado en herencia, permanecer en la línea de definición de clase en lugar del cuerpo de la clase donde deberían ir los datos.Aunque podría ser yo.
Por supuesto, cada situación es diferente, pero considere estas cosas que he dicho al decidir qué opción usar.
fuente
¡No diré mucho, excepto el código presente con comentarios que demuestran que super no significa llamar a base!
super != base.
En resumen, ¿qué se supone que significa "super" de todos modos? y entonces, ¿qué se supone que significa "base"?
Estas 2 reglas se aplican a la clase typedefs.
Considere el implementador de la biblioteca y el usuario de la biblioteca, ¿quién es súper y quién es la base?
Para obtener más información, aquí está el código de trabajo para copiar y pegar en su IDE:
Otro punto importante aquí es este:
en el interior
main()
utilizamos llamadas polimórficas hacia abajo que superan llamadas hacia arriba, no realmente útiles en la vida real, pero demuestran la diferencia.fuente
Yo uso la palabra clave __super. Pero es específico de Microsoft:
http://msdn.microsoft.com/en-us/library/94dw1w7x.aspx
fuente
Este es un método que uso que usa macros en lugar de typedef. Sé que esta no es la forma en C ++ de hacer las cosas, pero puede ser conveniente al encadenar iteradores juntos a través de la herencia cuando solo la clase base más alejada de la jerarquía está actuando sobre un desplazamiento heredado.
Por ejemplo:
La configuración genérica que utilizo hace que sea muy fácil de leer y copiar / pegar entre el árbol de herencia que tiene un código duplicado pero debe anularse porque el tipo de retorno debe coincidir con la clase actual.
Se podría usar una minúscula
super
para replicar el comportamiento visto en Java, pero mi estilo de codificación es usar todas las letras mayúsculas para las macros.fuente