2010-06-23 11 views
7

Por favor, considere el siguiente código:sobrecargado llamada de función virtual resolución

class Abase{}; 
class A1:public Abase{}; 
class A2:public A1{}; 
//etc 

class Bbase{ 
public: 
    virtual void f(Abase* a); 
    virtual void f(A1* a); 
    virtual void f(A2* a); 
}; 

class B1:public Bbase{ 
public: 
    void f(A1* a); 
}; 

class B2:public Bbase{ 
public: 
    void f(A2* a); 
}; 

int main(){ 
    A1* a1=new A1(); 
    A2* a2=new A2(); 
    Bbase* b1=new B1(); 
    Bbase* b2=new B2(); 
    b1->f(a1); // calls B1::f(A1*), ok 
    b2->f(a2); // calls B2::f(A2*), ok 
    b2->f(a1); // calls Bbase::f(A1*), ok 
    b1->f(a2); // calls Bbase::f(A2*), no- want B1::f(A1*)! 
} 

me interesa saber por qué C++ elige para resolver la llamada a la función en la última línea por upcasting la this puntero del objeto a la base clase, en lugar de upcasting el argumento de f()? ¿Hay alguna manera de que pueda obtener el comportamiento que quiero?

Respuesta

10

La elección de qué versión de f llamar se realiza mirando el tipo en tiempo de compilación del parámetro. El tipo de tiempo de ejecución no se considera para esta resolución de nombre. Como b1 es de tipo Bbase*, se consideran todos los miembros de Bbase; el que toma un A2* es la mejor coincidencia, por lo que es el que se llama.

+0

Gracias - según tengo entendido, el punto es que la resolución de qué virtual f() para llamar ocurre en tiempo de compilación, en función del argumento proporcionado a f(). Entonces en la última línea, el compilador ya ha decidido que se llamará f (A2 *). La versión de f (A2 *) llamada depende del tipo de tiempo de ejecución al que se apunta. Aquí, dado que la clase B1 no reemplaza a f (A2 *), se llama a la versión de la clase base. – stw

+0

@stw, absolutamente correcto. –

1
b1->f(static_cast<A1*>(a2)); 

Esto debería obligar al compilador a utilizar el método de sobrecarga con el parámetro de tipo A1.

+0

un dynamic_cast es más apropiado, creo. – Jason

+2

@Jason: un 'static_cast' a una clase base está bien. –

+2

Creo que un yeso estático está bien, ya que a) sabemos que el lanzamiento es correcto yb) es un lanzamiento ascendente, lo cual es seguro. –

0

Se llama nombre oculto. Cada f que declaras en una clase derivada sombrea cada f posible en cualquiera de sus clases base.

Usa un molde para la clase base para obtener el comportamiento deseado.

Cuando anula una función virtual, no anula las funciones sobrecargadas con el mismo nombre. Son funciones diferentes (y tienen diferentes entradas en el vtable).

+0

No, el problema aquí es al revés. Sería ocultar el nombre si llamaras a través de un puntero a una clase más derivada que oculta el (los) método (s) de la clase base. –

+0

ooops tienes razón, leo demasiado rápido. –

2

"... elige resolver la llamada de función en la última línea elevando el puntero del objeto a la clase base ...". ¿De qué estás hablando? En todas sus llamadas, el tipo de puntero de objeto es Bbase * y las funciones que las llamadas resuelven pertenecer a Bbase o sus descendientes. El compilador no realiza ningún upcasting para resolver sus llamadas. De hecho, las primeras dos llamadas requieren downcasting para llamar al overrider adecuado, ya que el overrider pertenece a la clase ubicada más abajo en la jerarquía. En cuanto a las dos últimas llamadas, se envían a la clase Bbase a través de un puntero del tipo Bbase *. Los tipos coinciden exactamente, no se produce ningún tipo de fundición.

En cuanto a la resolución de sobrecarga ... La resolución de sobrecarga es un proceso en tiempo de compilación, que se basa en los tipos estáticos de los argumentos y los rangos de posibles conversiones. Proporcionó un argumento del tipo A2 *. El candidato f(A2 *) coincidió con su argumento con precisión. El candidato f(A1 *) requiere una conversión adicional de A2 * a A1 *. El candidato que coincide exactamente se considera mejor, por lo que gana la resolución de sobrecarga. Sencillo.

+0

Lo siento: utilicé el término 'upcasting' de la manera incorrecta. Lo que quise decir es por qué el objeto se trata como un BBase aquí. La respuesta (como dices) es que la resolución de sobrecarga ocurre en tiempo de compilación, y en tiempo de compilación el objeto es un BBase, por lo que el compilador elige f (A2 *). – stw

0

Sus sobrecargas en Bbase para Abase y A2 están ocultas en B1. Tal vez puede solucionar ese problema así:

class Bbase{ 
public: 
    inline void f(Abase* a) { f_(a); } 
    inline void f(A1* a) { f_(a); } 
    inline void f(A2* a) { f_(a); } 
protected: 
    virtual void f_(Abase* a); 
    virtual void f_(A1* a); 
    virtual void f_(A2* a); 
}; 

class B1:public Bbase{ 
protected: 
    void f_(A1* a); 
}; 

class B2:public Bbase{ 
protected: 
    void f_(A2* a); 
}; 

o con una plantilla en Bbase:

class Bbase{ 
public: 
    template<class myA> 
    inline void f(myA* a) { f_(a); } 
protected: 
    virtual void f_(Abase* a); 
    virtual void f_(A1* a); 
    virtual void f_(A2* a); 
}; 
Cuestiones relacionadas