¿Es posible en C ++ tener una función miembro que sea ambas static
y virtual
? Aparentemente, no hay una forma directa de hacerlo ( static virtual member();
es un error de compilación), pero ¿hay al menos una forma de lograr el mismo efecto?
ES DECIR:
struct Object
{
struct TypeInformation;
static virtual const TypeInformation &GetTypeInformation() const;
};
struct SomeObject : public Object
{
static virtual const TypeInformation &GetTypeInformation() const;
};
Tiene sentido usar GetTypeInformation()
tanto en una instancia ( object->GetTypeInformation()
) como en una clase ( SomeObject::GetTypeInformation()
), lo que puede ser útil para las comparaciones y vital para las plantillas.
Las únicas formas en que puedo pensar implica escribir dos funciones / una función y una constante, por clase, o usar macros.
¿Alguna otra solución?
const
firma en un método marca elthis
puntero implícito como constante y no se puede aplicar a los métodos estáticos, ya que carecen del parámetro implícito.Respuestas:
No, no hay forma de hacerlo, ya que ¿qué pasaría cuando llamaras
Object::GetTypeInformation()
? No puede saber a qué versión de clase derivada llamar ya que no hay ningún objeto asociado con ella.Tendrá que convertirlo en una función virtual no estática para que funcione correctamente; si también desea poder llamar a una versión de clase derivada específica de manera no virtual sin una instancia de objeto, también deberá proporcionar una segunda versión estática redundante no virtual.
fuente
Muchos dicen que no es posible, iría un paso más allá y diría que no tiene sentido.
Un miembro estático es algo que no se relaciona con ninguna instancia, solo con la clase.
Un miembro virtual es algo que no se relaciona directamente con ninguna clase, solo con una instancia.
Entonces, un miembro virtual estático sería algo que no se relaciona con ninguna instancia o clase.
fuente
static virtual
método, pero un métodostatic
purovirtual
es muy significativo en una interfaz.static const string MyClassSillyAdditionalName
.Me encontré con este problema el otro día: tenía algunas clases llenas de métodos estáticos, pero quería usar la herencia y métodos virtuales y reducir la repetición de código. Mi solución fue:
En lugar de usar métodos estáticos, use un singleton con métodos virtuales.
En otras palabras, cada clase debe contener un método estático al que llame para obtener un puntero a una única instancia compartida de la clase. Puede hacer que los verdaderos constructores sean privados o protegidos para que el código externo no pueda usarlo incorrectamente creando instancias adicionales.
En la práctica, usar un singleton es muy parecido a usar métodos estáticos, excepto que puede aprovechar la herencia y los métodos virtuales.
fuente
¡Es posible!
Pero qué es exactamente posible, reduzcamos. La gente a menudo quiere algún tipo de "función virtual estática" debido a la duplicación del código necesario para poder llamar a la misma función a través de la llamada estática "SomeDerivedClass :: myfunction ()" y la llamada polimórfica "base_class_pointer-> myfunction ()". El método "legal" para permitir dicha funcionalidad es la duplicación de definiciones de funciones:
¿Qué pasa si la clase base tiene una gran cantidad de funciones estáticas y la clase derivada tiene que anular cada una de ellas y se olvidó de proporcionar una definición duplicada para la función virtual? Bien, obtendremos un extraño error durante el tiempo de ejecución que es difícil de rastrear. Porque la duplicación de código es algo malo. Lo siguiente intenta resolver este problema (y quiero decir de antemano que es completamente seguro y no contiene magia negra como typeid's o dynamic_cast's :)
Por lo tanto, queremos proporcionar solo una definición de getTypeInformation () por clase derivada y es obvio que debe ser una definición de estáticafuncionan porque no es posible llamar a "SomeDerivedClass :: getTypeInformation ()" si getTypeInformation () es virtual. ¿Cómo podemos llamar a la función estática de la clase derivada a través del puntero a la clase base? No es posible con vtable porque vtable almacena punteros solo para funciones virtuales y dado que decidimos no usar funciones virtuales, no podemos modificar vtable para nuestro beneficio. Luego, para poder acceder a la función estática de la clase derivada a través del puntero a la clase base, tenemos que almacenar de alguna manera el tipo de un objeto dentro de su clase base. Un enfoque es hacer que la clase base sea estandarizada usando un "patrón de plantilla curiosamente recurrente", pero no es apropiado aquí y usaremos una técnica llamada "borrado de tipo":
Ahora podemos almacenar el tipo de un objeto dentro de la clase base "Object" con una variable "keeper":
En una clase derivada, el guardián debe inicializarse durante la construcción:
Agreguemos azúcar sintáctico:
Ahora las declaraciones de descendientes se ven así:
uso:
Ventajas:
Desventajas
Problemas abiertos:
1) hay diferentes nombres para funciones estáticas y virtuales ¿cómo resolver la ambigüedad aquí?
2) ¿cómo llamar implícitamente OVERRIDE_STATIC_FUNCTIONS dentro de cada constructor?
fuente
Si bien Alsk ya ha dado una respuesta bastante detallada, me gustaría agregar una alternativa, ya que creo que su implementación mejorada es demasiado complicada.
Comenzamos con una clase base abstracta, que proporciona la interfaz para todos los tipos de objetos:
Ahora necesitamos una implementación real. Pero para evitar tener que escribir tanto los métodos estáticos como los virtuales, nuestras clases de objetos reales heredarán los métodos virtuales. Obviamente, esto solo funciona si la clase base sabe cómo acceder a la función miembro estática. Entonces necesitamos usar una plantilla y pasarle el nombre de la clase de objetos reales:
Finalmente, necesitamos implementar nuestros objetos reales. Aquí solo necesitamos implementar la función miembro estática, las funciones miembro virtuales serán heredadas de la clase de plantilla ObjectImpl, instanciadas con el nombre de la clase derivada, para que acceda a sus miembros estáticos.
Agreguemos un código para probar:
Anexo (12 de enero de 2019):
En lugar de utilizar la función GetClassNameStatic (), también puede definir el nombre de la clase como un miembro estático, incluso "en línea", que IIRC funciona desde C ++ 11 (no se asuste por todos los modificadores :)):
fuente
Es posible. Realiza dos funciones: estática y virtual
fuente
No, esto no es posible, porque las funciones miembro estáticas carecen de un
this
puntero. Y los miembros estáticos (tanto funciones como variables) no son realmente miembros de clase per-se. Simplemente son invocados porClassName::member
y se adhieren a los especificadores de acceso de clase. Su almacenamiento se define en algún lugar fuera de la clase; el almacenamiento no se crea cada vez que crea una instancia de un objeto de la clase. Los punteros a los miembros de la clase son especiales en semántica y sintaxis. Un puntero a un miembro estático es un puntero normal en todos los aspectos.Las funciones virtuales en una clase necesitan el
this
puntero y están muy acopladas a la clase, por lo tanto, no pueden ser estáticas.fuente
this
puntero. Las funciones estáticas no son específicas de una instancia y no lo necesitarían. Entonces, esa no es una razón por la que los miembros estáticos virtuales son imposibles.Bueno, una respuesta bastante tardía, pero es posible usando el patrón de plantilla curiosamente recurrente. Este artículo de Wikipedia tiene la información que necesita y también el ejemplo bajo polimorfismo estático es lo que se le solicita.
fuente
Creo que lo que intentas hacer se puede hacer a través de plantillas. Estoy tratando de leer entre líneas aquí. Lo que está tratando de hacer es llamar a un método desde algún código, donde llama a una versión derivada pero la persona que llama no especifica qué clase. Ejemplo:
Desea que Try () llame a la versión Bar de M sin especificar Bar. La forma de hacerlo para las estadísticas es usar una plantilla. Así que cámbialo así:
fuente
No, la función miembro estática no puede ser virtual, ya que el concepto virtual se resuelve en tiempo de ejecución con la ayuda de vptr, y vptr es miembro no estático de una clase. No seas virtual.
fuente
No es posible, pero eso es solo por una omisión. No es algo que "no tenga sentido" como mucha gente parece reclamar. Para ser claros, estoy hablando de algo como esto:
Esto es 100% algo que podría implementarse (simplemente no lo ha hecho), y diría que es algo útil.
Considere cómo funcionan las funciones virtuales normales. Elimine el
static
sy agregue algunas otras cosas y tenemos:Esto funciona bien y, básicamente, lo que sucede es que el compilador crea dos tablas, llamadas VTables, y asigna índices a las funciones virtuales como esta
A continuación, cada clase con funciones virtuales se aumenta con otro campo que apunta a su VTable, por lo que el compilador básicamente las cambia para que sean así:
Entonces, ¿qué sucede realmente cuando llamas
b->sayMyName()
? Básicamente esto:(El primer parámetro se convierte
this
).Bien, ¿cómo funcionaría con funciones virtuales estáticas? ¿Cuál es la diferencia entre las funciones miembro estáticas y no estáticas? La única diferencia es que este último obtiene un
this
puntero.Podemos hacer exactamente lo mismo con funciones virtuales estáticas: simplemente quite el
this
puntero.Esto podría admitir ambas sintaxis:
Así que ignora a todos los detractores. Se hace tiene sentido. ¿Por qué no es compatible entonces? Creo que es porque tiene muy pocos beneficios e incluso podría ser un poco confuso.
La única ventaja técnica sobre una función virtual normal es que no necesita pasar
this
a la función, pero no creo que eso suponga una diferencia apreciable en el rendimiento.Significa que no tiene una función estática y no estática separada para los casos en que tiene una instancia y cuando no tiene una instancia, pero también puede ser confuso que solo sea realmente "virtual" cuando usa la llamada de instancia
fuente
No, no es posible, ya que los miembros estáticos están vinculados en tiempo de compilación, mientras que los miembros virtuales están vinculados en tiempo de ejecución.
fuente
Primero, las respuestas son correctas de que lo que solicita el OP es una contradicción de términos: los métodos virtuales dependen del tipo de tiempo de ejecución de una instancia; Las funciones estáticas específicamente no dependen de una instancia, solo de un tipo. Dicho esto, tiene sentido que las funciones estáticas devuelvan algo específico a un tipo. Por ejemplo, tenía una familia de clases MouseTool para el patrón State y comencé a hacer que cada una tuviera una función estática que devolvía el modificador de teclado que lo acompañaba; Usé esas funciones estáticas en la función de fábrica que hicieron la instancia correcta de MouseTool. Esa función verificó el estado del mouse contra MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier (), etc. y luego instancia el apropiado. Por supuesto, más tarde quería comprobar si el estado era correcto, así que quería escribir algo como "
Entonces, si te encuentras con ganas de esto, es posible que desees volver a buscar tu solución. Aún así, entiendo el deseo de tener métodos estáticos y luego llamarlos dinámicamente en función del tipo dinámico de una instancia. Creo que el Patrón de visitante puede darte lo que quieres. Te da lo que quieres. Es un poco de código extra, pero podría ser útil para otros visitantes.
Ver: http://en.wikipedia.org/wiki/Visitor_pattern para antecedentes.
Luego para cada objeto concreto:
y luego defina el visitante base:
Luego, el visitante concreto que selecciona la función estática adecuada:
finalmente, úsalo:
Notas:
Si desea evitar errores de copiar y pegar donde uno de sus métodos de visita llama a la función estática incorrecta, puede usar una función auxiliar con plantilla (que no puede ser virtual) a su visitante con una plantilla como esta:
fuente
Foo foo; ... foo::bar();
lugar deFoo::bar();
). Eso no es diferente,decltype(foo)::bar();
pero eso nuevamente estaría estáticamente vinculado. El enfoque de visitante parece una forma razonable de obtener este comportamiento sin simplemente hacer que el método estático sea un método const virtual.Con c ++ puede usar herencia estática con el método crt. Por ejemplo, se usa ampliamente en la plantilla de ventana atl & wtl.
Ver https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
Para ser simple, tiene una clase que se crea en sí misma como class myclass: public myancestor. Desde este punto, la clase myancestor ahora puede llamar a su función T :: YourImpl estática.
fuente
Quizás puedas probar mi solución a continuación:
fuente
Base::mSelf
se refiere a la instancia MÁS RECIENTEMENTE construida de cualquier clase derivada, incluso si esa instancia ha sido destruida . Asíclass D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */
que NO es lo que se quiere.Como otros han dicho, hay 2 piezas importantes de información:
this
puntero al hacer una llamada de función estática ythis
puntero apunta a la estructura donde se usa la tabla virtual, o thunk, para buscar qué método de tiempo de ejecución llamar.Una función estática se determina en tiempo de compilación.
Mostré este ejemplo de código en miembros estáticos de C ++ en clase ; muestra que puede llamar a un método estático dado un puntero nulo:
fuente
p < null
,p >= null
etc., todos están indefinidos también.)