Es un puntero a una función miembro virtual válida en el constructor de la clase base?

StackOverflow https://stackoverflow.com/questions/3092763

  •  29-09-2019
  •  | 
  •  

Pregunta

Mi pregunta no se trata de llamando una función miembro virtual desde un constructor de la clase base, pero si el puntero a una función miembro virtual es válida en el constructor de la clase base.

Dada la siguiente

class A
{
    void (A::*m_pMember)();

public:
    A() :
        m_pMember(&A::vmember)
    {
    }

    virtual void vmember()
    {
        printf("In A::vmember()\n");
    }

    void test()
    {
        (this->*m_pMember)();
    }
};

class B : public A
{
public:
    virtual void vmember()
    {
        printf("In B::vmember()\n");
    }
};

int main()
{
    B b;
    b.test();

    return 0;
}

Será este producto "B :: En vmember ()" para todos los compiladores C ++ compatible?

¿Fue útil?

Solución

El puntero es válido, sin embargo hay que tener en cuenta que cuando una función virtual se invoca a través de un puntero que siempre se resuelve de acuerdo con la dinámico tipo de objeto que se utiliza en la izquierda lado. Esto significa que cuando se invoca una función virtual desde el constructor, no importa si se invoca directamente o si se invoca a través de un puntero. En ambos casos, la llamada se resolverá con el tipo cuyo constructor está trabajando actualmente. Así es como virtuales funciones de trabajo, cuando invocarlos durante la construcción objeto (o destrucción).

Tenga en cuenta también que los punteros a funciones miembro son generalmente no unidas a funciones específicas en el punto de initalization. Si la función objetivo es no virtual, que se puede decir que el puntero apunta a una función específica. Sin embargo, si la función objetivo es virtual, no hay manera de decir, cuando el puntero está apuntando. Por ejemplo, la especificación del lenguaje establece explícitamente que cuando se compara (para la igualdad) dos punteros que le suceden a punto a las funciones virtuales, el resultado es no especificado .

Otros consejos

"válido" es un término específico cuando se aplica a los punteros. punteros de datos son válidos cuando apuntan a un objeto o NULL; punteros de función son válidos cuando apuntan a una función o NULL y punteros a los miembros son válidas cuando el punto a un miembro o NULL.

Sin embargo, a partir de una pregunta sobre la producción real, puedo inferir que quería pedir algo más. Echemos un vistazo a su función vmember - o mejor dicho, las funciones? Es obvio que hay dos cuerpos de las funciones. Que podría haber hecho sólo el derivado virtual, por lo que también confirma que hay realmente dos funciones vmember tanto, que sucede a ser virtual.

Ahora, la pregunta es si al tomar la dirección de una función miembro ya se elige la función real. Sus implementaciones muestran que no lo hacen, y que esto sólo sucede cuando el puntero se eliminan las referencias realidad.

La razón de que debe trabajar de esta manera es trivial. Tomar la dirección de una función miembro no implica un objeto real, algo que sería necesario para resolver la llamada virtual. Te voy a enseñar:

namespace {
  void (A::*test)() = &A::vmember;
  A a;
  B b;
  (a.*test)();
  (b.*test)();
}

Cuando inicializamos test, no hay ningún objeto de A tipo o B en absoluto, sin embargo, es posible tomar la dirección de &A::vmember. Ese mismo puntero miembro continuación, se puede utilizar con dos objetos diferentes. ¿Qué podría este producto, pero "In A :: vmember () \ n" y "B :: En vmember () \ n"?

este artículo para una discusión en profundidad de los punteros de función miembro y la forma de utilizarlos. Esto debe responder a todas sus preguntas.

He encontrado una pequeña explicación sobre el Viejo cosa nueva (un blog de Raymond Chen, a veces referido como de Microsoft Chuck Norris).

Por supuesto que no dice nada sobre el cumplimiento, pero explica por qué:

B b;

b.A::vmember(); // [1]

(b.*&A::vmember)(); // [2]

1 y 2 en realidad invocar una función diferente ... que es bastante sorprendente, la verdad. También significa que usted no puede realmente evitar los envíos en tiempo de ejecución utilizando un puntero a la función miembro: /

Yo no creo. Puntero a la función miembro virtual se resuelve a través de VMT, por lo que de la misma manera como llamada a esta función sucedería. Esto significa que no es válida, ya que VMT se llena después de terminado el constructor.

OMI es implementation defined para tomar la dirección de una función virtual. Esto se debe a que las funciones virtuales se implementan utilizando vtables que son compilador específico de la implementación. Desde la viable, no se garantiza que sea completa hasta que se realiza la ejecución de la clase Héctor, un puntero a una entrada en una tabla de este tipo (función virtual) puede ser implementation defined behavior.

No es una pregunta algo relacionado que le pregunté sobre SO aquí unos meses; que básicamente dice tomando dirección de la función virtual no está especificado en el estándar de C ++.

Así que, en cualquier caso, incluso si funciona para usted, la solución no va a ser portátil.

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