¿Cómo obtengo un puntero de función para una función miembro de clase y luego llamo a esa función miembro con un objeto específico? Me gustaría escribir:
class Dog : Animal
{
Dog ();
void bark ();
}
…
Dog* pDog = new Dog ();
BarkFunction pBark = &Dog::bark;
(*pBark) (pDog);
…
Además, si es posible, también me gustaría invocar el constructor a través de un puntero:
NewAnimalFunction pNew = &Dog::Dog;
Animal* pAnimal = (*pNew)();
¿Es esto posible y, de ser así, cuál es la forma preferida de hacerlo?
c++
function-pointers
class-method
Tony el pony
fuente
fuente
Dog dog;
Es más probable que lo que desee sea un objeto automático simple ( ).Respuestas:
Lea esto para obtener más detalles:
fuente
*this.*pt2Member
funcionaría.*
tiene mayor precedencia sobre.*
... Personalmente, todavía habría escritothis->*pt2Member
, es un operador menos.pt2ConstMember
aNULL
?(object.*method_pointer)
, por lo que queremos*
que tenga mayor prioridad.this
solo se usa para demostrar que todo lo que aplique.*
debe ser un puntero a una instancia de la (sub) clase. Sin embargo, esta es una nueva sintaxis para mí, solo supongo que basándome en otras respuestas y recursos vinculados aquí. Sugiero una edición para que quede más claro.Es más fácil comenzar con un
typedef
. Para una función miembro, agrega el nombre de clase en la declaración de tipo:Luego, para invocar el método, usa el
->*
operador:No creo que puedas trabajar con constructores como este, los ctors y dtors son especiales. La forma normal de lograr ese tipo de cosas sería usando un método de fábrica, que es básicamente una función estática que llama al constructor por usted. Consulte el código a continuación para ver un ejemplo.
He modificado su código para hacer básicamente lo que describe. Hay algunas advertencias a continuación.
Ahora bien, aunque normalmente puede usar un
Dog*
en lugar de unAnimal*
gracias a la magia del polimorfismo, el tipo de puntero de función no sigue las reglas de búsqueda de la jerarquía de clases. Por lo tanto, un puntero de método Animal no es compatible con un puntero de método Dog, en otras palabras, no puede asignar unDog* (*)()
a una variable de tipoAnimal* (*)()
.El
newDog
método estático es un ejemplo simple de una fábrica, que simplemente crea y devuelve nuevas instancias. Al ser una función estática, tiene una función regulartypedef
(sin calificador de clase).Habiendo respondido lo anterior, me pregunto si no existe una mejor manera de lograr lo que necesita. Hay algunos escenarios específicos en los que haría este tipo de cosas, pero es posible que encuentre otros patrones que funcionan mejor para su problema. Si describe en términos más generales lo que está tratando de lograr, ¡la mente colmena puede resultar aún más útil!
En relación con lo anterior, sin duda encontrará muy útiles la biblioteca de enlaces Boost y otros módulos relacionados.
fuente
->*
antes, pero ahora espero que nunca lo necesite :)No creo que nadie haya explicado aquí que un problema es que necesita " punteros a miembros " en lugar de punteros a funciones normales.
Los apuntadores de miembros a funciones no son simplemente apuntadores de funciones. En términos de implementación, el compilador no puede usar una dirección de función simple porque, en general, no conoce la dirección a la que llamar hasta que sepa qué objeto desreferenciar (piense en funciones virtuales). También necesita conocer el objeto para proporcionar el
this
parámetro implícito, por supuesto.Habiendo dicho que los necesita, ahora diré que realmente necesita evitarlos. En serio, los consejos para miembros son un fastidio. Es mucho más sensato mirar patrones de diseño orientados a objetos que logren el mismo objetivo, o usar uno
boost::function
o lo que sea como se mencionó anteriormente, suponiendo que pueda tomar esa decisión, claro.Si está proporcionando ese puntero de función a código existente, por lo que realmente necesita un puntero de función simple, debe escribir una función como miembro estático de la clase. Una función miembro estática no comprende
this
, por lo que deberá pasar el objeto como un parámetro explícito. Hubo una vez un modismo no tan inusual en este sentido para trabajar con código C antiguo que necesita punteros de funciónDado
myfunction
que en realidad es solo una función normal (dejando de lado los problemas del alcance), se puede encontrar un puntero de función en la forma C normal.EDITAR : este tipo de método se denomina "método de clase" o "función miembro estática". La principal diferencia con una función que no es miembro es que, si hace referencia a ella desde fuera de la clase, debe especificar el alcance utilizando el
::
operador de resolución de alcance. Por ejemplo, para obtener el puntero de función, use&myclass::myfunction
y para llamarlo usemyclass::myfunction (arg);
.Este tipo de cosas es bastante común cuando se utilizan las antiguas API de Win32, que fueron diseñadas originalmente para C en lugar de C ++. Por supuesto, en ese caso, el parámetro normalmente es LPARAM o similar en lugar de un puntero, y se necesita algo de conversión.
fuente
fuente
Ejemplo ejecutable mínimo
main.cpp
Compilar y ejecutar:
Probado en Ubuntu 18.04.
No puede cambiar el orden de los paréntesis ni omitirlos. Lo siguiente no funciona:
GCC 9.2 fallaría con:
Estándar C ++ 11
.*
y->*
son operadores únicos introducidos en C ++ para este propósito, y no están presentes en C.Borrador estándar de C ++ 11 N3337 :
.*
y->*
.fuente
Vine aquí para aprender cómo crear un puntero de función (no un puntero de método) a partir de un método, pero ninguna de las respuestas aquí proporciona una solución. Esto es lo que se me ocurrió:
Entonces, para su ejemplo, ahora haría:
Editar:
usando C ++ 17, hay una solución aún mejor:
que se puede utilizar directamente sin la macro:
Para métodos con modificadores como,
const
es posible que necesite algunas especializaciones más como:fuente
La razón por la que no puede usar punteros de función para llamar a funciones miembro es que los punteros de función ordinarios suelen ser solo la dirección de memoria de la función.
Para llamar a una función miembro, necesita saber dos cosas:
Los punteros de función ordinaria no pueden almacenar ambos. Los punteros de función miembro de C ++ se utilizan para almacenar a), por lo que es necesario especificar la instancia explícitamente al llamar a un puntero de función miembro.
fuente
Un puntero de función a un miembro de clase es un problema que es realmente adecuado para usar boost :: function. Pequeño ejemplo:
fuente
Para crear un nuevo objeto, puede usar la ubicación nueva, como se mencionó anteriormente, o hacer que su clase implemente un método clone () que cree una copia del objeto. Luego, puede llamar a este método de clonación utilizando un puntero de función miembro como se explicó anteriormente para crear nuevas instancias del objeto. La ventaja de la clonación es que a veces puede estar trabajando con un puntero a una clase base donde no conoce el tipo de objeto. En este caso, un método clone () puede ser más fácil de usar. Además, clone () le permitirá copiar el estado del objeto si eso es lo que desea.
fuente
Hice esto con std :: function y std :: bind ..
Escribí esta clase EventManager que almacena un vector de controladores en un mapa unordered_map que asigna tipos de eventos (que son solo const unsigned int, tengo una gran enumeración de espacios de nombres de ellos) a un vector de controladores para ese tipo de evento.
En mi clase EventManagerTests, configuré un controlador de eventos, como este:
Aquí está la función AddEventListener:
Aquí está la definición de tipo EventHandler:
Luego, de vuelta en EventManagerTests :: RaiseEvent, hago esto:
Aquí está el código para EventManager :: RaiseEvent:
Esto funciona. Recibo la llamada en EventManagerTests :: OnKeyDown. Tengo que eliminar los vectores en tiempo de limpieza, pero una vez que lo hago no hay fugas. Generar un evento toma alrededor de 5 microsegundos en mi computadora, que es alrededor de 2008. No exactamente súper rápido, pero. Bastante justo siempre que sepa eso y no lo use en código ultra caliente.
Me gustaría acelerarlo rodando mi propia std :: function y std :: bind, y tal vez usando una matriz de matrices en lugar de un unordered_map de vectores, pero no he descubierto cómo almacenar una función miembro puntero y llamarlo desde un código que no sabe nada sobre la clase que se llama. La respuesta de Eyelash parece muy interesante.
fuente