Pregunta

Estoy acostumbrado a pensar en las funciones miembro como un caso especial de funciones normales, donde las funciones miembro tienen un parámetro adicional al comienzo de su lista de parámetros para el puntero 'this', es decir, el objeto sobre el cual se supone que la función miembro debe actuar. He usado boost :: function de esta manera en el pasado y nunca encontré ningún problema:

boost::function f<(void)(MyObject*, int, int)> = &MyObject::method_that_takes_two_ints;

Pero he visto esta sintaxis para punteros de función miembro:

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

En esta sintaxis, el parámetro 'this' no es visible. Lo que me hizo preguntarme si bajo el capó las funciones de puntero a miembro son realmente una bestia separada, y ese impulso fue cuidar los detalles para mí.

¿Qué dicta el estándar sobre la ubicación del parámetro 'this'? ¿Quizás solo en mi compilador el argumento adicional 'this' viene primero, y tal vez en otros compiladores podría estar al final? ¿Tengo suerte de que mi forma de pensar sea coherente con la forma en que lo manejan mis compiladores (GCC4, VS2005)? ¿Las funciones de puntero a miembro siempre son solo un caso especial de funciones de puntero a con un parámetro adicional o puede el compilador implementarlas de manera diferente?

¿Fue útil?

Solución

El estándar no dice casi nada acerca de dónde se debe colocar el puntero this , y de hecho es bastante común usar una convención de llamada diferente para las funciones de los miembros. (Por lo tanto, el puntero 'this' no es solo un primer argumento adicional, sino que se almacena en una ubicación diferente de la que suele tener el primer argumento)

En particular, MSVC utiliza la convención de llamada thiscall para las funciones de los miembros, y stdcall en otros lugares. http://www.hackcraft.net/cpp/MSCallingConventions/#thiscall describe las diferencias entre ellos, pero tenga en cuenta que thiscall almacena el puntero this en el registro ECX , mientras que stdcall almacena < em> todos parámetros en la pila.

Definitivamente es mejor tratarlos como tipos completamente distintos. Un puntero a una función miembro es no solo un puntero a una función con un parámetro adicional.

Otros consejos

El puntero this no se almacena junto con un puntero a miembro (los punteros de funciones de miembro son un caso especial de esto). Si solo lo haces

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

entonces lo que se almacena es solo la información a la que se debe llamar la función miembro en un objeto que luego debe proporcionar. Si desea llamarlo, debe pasar un objeto, de donde el compilador obtendrá el puntero this .

MyObject o; (o.*f)(1, 2);

Un puntero de función miembro es solo un puntero miembro cuyo tipo (que se señala) es un tipo de función. El Estándar dice que los punteros de funciones miembro no tienen su propio "tipo de función miembro" que señalan y que de alguna manera incluirían este tipo de puntero.

int main() {
    typedef void fun() const;
    fun MyObject::*mem_function_ptr = 
        &MyObject::const_method_that_takes_two_ints;
}

fun en ese código es el tipo de función. El tipo que es "normal" La función tiene. Un puntero a función, en oposición a un puntero de función miembro, es solo un puntero a una función que tiene ese tipo:

void foo() { cout << "hello"; }
int main() {
    typedef void fun();
    fun * f = &foo;
}

Mientras que una función de puntero a miembro tiene el nivel adicional de puntero de miembro en la parte superior de ese tipo de función.

Algo sobre el puntero this y cómo se relaciona con el objeto al que apunta (no técnico, solo lo teórico):

Cada función miembro tiene un parámetro oculto llamado parámetro de objeto implícito que tiene el tipo MyObject & amp; o MyObject const & amp; dependiendo de si tiene una función miembro const o nonconst. El objeto al que llama la función miembro, o , es el argumento de objeto implícito , que se pasa al parámetro. En la teoría del estándar que conforma las reglas que describen cómo se llaman las funciones miembro, el parámetro objeto implícito es un primer parámetro oculto. Eso es conceptual y no significa que sea el caso real en las implementaciones. El argumento del objeto implícito se vincula a ese parámetro de objeto implícito, posiblemente causando conversiones implícitas (por lo tanto, si llama a una función miembro const en un objeto no const, una conversión de calificación se convierte de MyObject a MyObject const & amp; . Eso es lo que hace que las funciones no const sean una mejor opción que las funciones const para llamar, para un objeto no const). Por ejemplo, se puede decir en este código:

struct A {
    operator int() const { return 0; }
};

int main() { 
    A a;
    int i = a; // implicit conversion using the conversion function
}

Que el argumento del objeto implícito a de tipo A está vinculado al parámetro de objeto implícito de tipo A const & amp; , cuyo objeto es entonces señalado por el puntero this que tiene el tipo A const * aquí. Es importante tener en cuenta que el parámetro objeto implícito es solo una construcción teórica, para formalizar cómo se componen las reglas para llamar a una función miembro (y los constructores no las incluyen), mientras que este puntero es realmente existente. this es un puntero, porque cuando se introdujo this , C ++ aún no tenía referencias.

Espero que eso te ayude a entender el asunto.

Un excelente artículo sobre punteros de funciones miembro es Punteros de función miembro y los C ++ más rápidos posibles de CodeProject. Delegados . Este artículo describe punteros de funciones miembro desde los casos simples hasta punteros virtuales de funciones miembro con herencia múltiple. Como beneficio adicional, proporciona una implementación de delegados que puede ser realmente útil.

Sí, los punteros a funciones y los punteros a miembros son bestias completamente diferentes. Los punteros a los miembros deben recibir una instancia de objeto para ser desreferenciada utilizando los operadores - > * o . * . No se utiliza el parámetro this al hacer un puntero a miembro porque this se determina cuando se usa el puntero a miembro (el objeto a la izquierda de - > * o . * ).

Tenga en cuenta que probablemente haya menos diferencia entre una función de puntero a miembro y una variable de puntero a miembro que entre una función de puntero a miembro y un puntero de función regular.

Normalmente, las funciones miembro y las funciones regulares pueden tener convenciones de llamada completamente diferentes, por lo que no puede emitir entre ellas.

Tenga en cuenta que el tamaño de una función de puntero a miembro puede ser diferente usando diferentes compiladores.

Otra cosa a tener en cuenta, como está escrito en The Old New Cosa blog :

  

El tamaño de un   la función de puntero a miembro puede cambiar   dependiendo de la clase.

Definitivamente son tipos distintos y cualquier suposición que haga será específica de la plataforma / compilador.

Esta página tiene más información sobre la implementación de puntos de función miembro que Siempre quise saber, incluidos los detalles de implementación de numerosos compiladores populares.

Para responder todas las preguntas: Sí, son punteros especiales, diferentes de los punteros comunes. Sí, la función boost :: los reconoce.

El estándar no dice nada sobre los detalles internos de las pilas de llamadas. De hecho, muchos compiladores pueden usar registros enteros, registros de coma flotante y / o la pila, dependiendo de la lista de argumentos real. Un puntero 'this' es solo un caso especial más.

La función Boost :: resuelve esto usando dos rutas de código internamente. Puede ver esto inspeccionando la pila de llamadas para los dos casos. Si su función boost :: almacena un puntero a la función miembro, el operador () dividirá la lista de argumentos. El primer argumento se usa como el objeto en el que se llama a la función miembro con los argumentos restantes.

Para complementar la respuesta de todos los demás, Boost.Function funciona especializando el operador de asignación en punteros de funciones de miembro, para permitirle detectar cuándo ha pasado uno. Cuando llame a esa función, la reinterpretará internamente al método apropiado para llamar al puntero de la función miembro ( (obj- > * fun) (args) ).

Creo que puede encontrar este enlace bastante interesante:

http://www.parashift.com/c++ -faq-lite / pointers-to-members.html

Es una muy buena descripción de todo lo que le gustaría saber sobre puntero a miembros.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top