punteros puntero matrices declaracion cadenas aritmetica c++ pointers function-pointers member-function-pointers

c++ - matrices - punteros en c



¿Por qué el tamaño de un puntero a una función es diferente del tamaño de un puntero a una función miembro? (5)

¿No es un puntero solo una dirección? ¿O me estoy perdiendo algo?

Probé con varios tipos de punteros:

  • los punteros a cualquier variable son los mismos (8B en mi plataforma)
  • los punteros a las funciones son del mismo tamaño, como punteros a las variables (8B nuevamente)
  • apunta a funciones con diferentes parámetros, sigue siendo el mismo (8B)

PERO los punteros a las funciones de los miembros son más grandes: 16B en mi plataforma.

Tres cosas:

  1. ¿Por qué los punteros a las funciones miembro son más grandes? ¿Qué más información necesitan?
  2. Hasta donde yo sé, el estándar no dice nada sobre el tamaño de un puntero, excepto que el void* debe ser capaz de "contener" cualquier tipo de puntero. En otras palabras, cualquier puntero debe poder convertirse en void* , ¿verdad? Si es así, ¿por qué sizeof( void* ) es 8, mientras que sizeof un puntero a la función miembro es 16?
  3. ¿Hay algún otro ejemplo para los punteros, que son de diferente tamaño (es decir, para las plataformas estándar , no algunas raras y especiales)?

Algunas de las principales razones para representar punteros a funciones miembro como {this, T (*f)()} son:

  • Tiene una implementación más simple en el compilador que implementar punteros a funciones miembro como T (*f)()

  • No implica generación de código de tiempo de ejecución ni contabilidad adicional

  • Se comporta razonablemente bien en comparación con T (*f)()

  • No hay suficiente demanda de los programadores de C ++ para que el tamaño de los punteros a las funciones de los miembros sea igual a sizeof(void*)

  • La generación de código de tiempo de ejecución durante la ejecución es de facto un tabú para el código C ++ actualmente


Algunas explicaciones se pueden encontrar aquí: La representación subyacente de punteros de función de miembro

Aunque los indicadores para los miembros se comportan como indicadores comunes, detrás de escena su representación es bastante diferente. De hecho, un puntero a miembro generalmente consiste en una estructura que contiene hasta cuatro campos en ciertos casos. Esto se debe a que los punteros a los miembros tienen que admitir no solo funciones de miembro ordinarias, sino también funciones de miembros virtuales, funciones de miembros de objetos que tienen múltiples clases base y funciones de miembro de clases base virtuales. Por lo tanto, la función miembro más simple se puede representar como un conjunto de dos punteros: uno que contiene la dirección de memoria física de la función miembro, y un segundo puntero que contiene este puntero. Sin embargo, en casos como una función de miembro virtual, herencia múltiple y herencia virtual, el puntero al miembro debe almacenar información adicional. Por lo tanto, no puede enviar punteros a los miembros a punteros ordinarios ni puede lanzar con seguridad punteros a miembros de diferentes tipos. Blockquote


Básicamente porque necesitan soportar el comportamiento polimórfico. Vea un buen article de Raymond Chen.


Supongo que tiene algo que ver con this puntero ... Es decir, cada función miembro también debe tener el puntero de la clase en la que se encuentran. El puntero hace que la función sea un poco más grande.


EDITAR : Me di cuenta de que todavía recibo votos este mes después, aunque mi respuesta original es mala y engañosa (ni siquiera puedo recordar lo que estaba pensando en ese momento, y no tiene mucho sentido) !) así que pensé en intentar y aclarar la situación, ya que las personas todavía deben llegar aquí a través de la búsqueda.

En la situación más normal, puedes pensar en

struct A { int i; int foo() { return i; } }; A a; a.foo();

como

struct A { int i; }; int A_foo( A* this ) { return this->i; }; A a; A_foo(&a);

(Empezando a parecerse a C , ¿verdad?) Entonces, usted pensaría que el puntero &A::foo serían los mismos que los punteros de una función normal. Pero hay un par de complicaciones: herencia múltiple y funciones virtuales.

Entonces imagina que tenemos:

struct A {int a;}; struct B {int b;}; struct C : A, B {int c;};

Podría ser presentado así:

Como puede ver, si quiere apuntar al objeto con A* o C* , apunte al inicio, pero si quiere apuntarlo con B* , tiene que apuntar a algún lugar en el medio. Entonces, si C hereda alguna función miembro de B y desea apuntar a ella, entonces llama a la función en una C* , necesita saber mezclar this puntero. Esa información necesita ser almacenada en alguna parte. Entonces se agrupa con el puntero a la función.

Ahora, para cada clase que tiene funciones virtual , el compilador crea una lista de ellas llamada tabla virtual . A continuación, agrega un puntero adicional a esta tabla a la clase ( vptr ). Entonces para esta estructura de clase:

struct A { int a; virtual void foo(){}; }; struct B : A { int b; virtual void foo(){}; virtual void bar(){}; };

El compilador podría terminar haciéndolo así:

Por lo tanto, un puntero de función miembro a una función virtual realmente necesita ser un índice en la tabla virtual. Por lo tanto, un puntero de función miembro realmente necesita 1) posiblemente un puntero a la función, 2) posiblemente un ajuste de this puntero, y 3) posiblemente un índice vtable. Para ser coherente, cada puntero de función miembro debe ser capaz de todos estos. Así que eso es 8 bytes para el puntero, 4 bytes para el ajuste, 4 bytes para el índice, para 16 bytes en total.

Creo que esto es algo que realmente varía mucho entre los compiladores, y hay muchas optimizaciones posibles. Probablemente ninguno realmente lo implementa de la manera que he descrito.

Para obtener muchos detalles, consulte this (vaya a "Implementaciones de punteros de funciones de miembro").