2012-01-23 13 views
16

Consideremos el siguiente ejemplo:anular una función miembro con diferente tipo de retorno

#include <iostream> 

using namespace std; 

class base 
{ 
    public: 
     virtual int func() 
     { 
     cout << "vfunc in base class\n"; 
     return 0; 
     } 
}; 

class derived: public base 
{ 
    public: 
     double func() 
     { 
     cout << "vfunc in derived class\n"; 
     return 0; 
     } 
}; 

int main() 
{ 
    base *bptr = new derived; 
    bptr->func(); 

    return 0; 
} 

El compilador da un error para el código de seguridad que no es de tipo conflictivo para la función overriden. ¿Por qué no es posible anular una función en la clase derivada con un tipo de devolución diferente?

Creo que, para anular una función, el método virtual de la clase base debe redefinirse en la clase derivada. Para redefinir un método, las firmas de los métodos tienen que ser iguales. Como el tipo de devolución no forma parte de la firma, creo que incluso si hay una diferencia en el tipo de devolución, el método aún se redefinirá. En ese caso para el código anterior, la función virtual func se redefine en la clase derivada con un tipo de devolución diferente. Pero el compilador arroja un error. Es mi entendimiento correcto?

+0

Por el bien de la clarificación, lo compilador le está dando que error? –

+0

@SionSheevok, GCC hace al menos: http://codepad.org/z7rXpCeK – bdonlan

+0

@SionSheevok: Estoy usando gcc 3.4.6 –

Respuesta

17

Anulación básicamente significa que se llamará al método de clase base o al método de clase derivada en tiempo de ejecución según el objeto real apuntado por el puntero .
Implica que:
es decir: Todo lugar donde el método de la clase base puede ser llamada puede ser sustituida por la llamada a método de clase derivada sin ningún cambio de código de llamada.

Para lograr esto, la única forma posible es restringir los tipos de devolución de los métodos virtuales primordiales para devolver el mismo tipo que la clase Base o un tipo derivado de eso (tipos de devolución co-variante) y la Norma aplica esta condición.

Si la condición anterior no estaba en su lugar, dejaría una ventana para romper el código existente mediante la adición de nuevas funcionalidades.

+2

Als: 'tipo derivado de eso (tipos de devolución co-variante)'. Eso fue crítico para mi comprensión. Buena explicación. :) –

7

Para anular una función virtual, el valor de retorno debe ser exactamente el mismo *. C++ será no automáticamente convierta entre double y int aquí - después de todo, ¿cómo sabría qué tipo de devolución desea cuando llama desde un puntero de clase derivado? Tenga en cuenta que si cambia parte de la firma (parámetros, const-ness, etc.), también puede cambiar el valor de retorno.

* - estrictamente hablando, debe ser 'covariante'. Lo que esto significa es que el tipo que devuelve debe ser un subconjunto del tipo de devolución de la función principal. Por ejemplo, si la clase padre devuelve un base *, puede devolver un derived *. Como derived s son simultáneamente también base s, el compilador le permite anular de esta manera. Pero no puede devolver tipos totalmente no relacionados, como int y double; solo porque haya una conversión implícita no significa que el compilador te permita hacer este tipo de anulación.

+0

'Tenga en cuenta que si cambia parte de la firma (parámetros, const-ness, etc.), entonces también puede cambiar el valor de retorno'. Si lo hago, entonces la función no se anula ¿verdad? –

+3

Exactamente la misma o una subclase, creo que te refieres. – Nemo

+1

tipos de devolución 'co-variant' están permitidos para funciones virtuales. –

1

Ver this question. En resumen, solo puede anular una función virtual utilizando un tipo de devolución diferente si los tipos son covariant.

+0

Los * tipos * no son covariantes, el * reemplazo * es. –

+0

¿El howto I linked obtiene la semántica incorrecta? – ezod

+0

No, solo su interpretación es un poco idiosincrásica. –

0

La anulación no es posible, ya que las firmas son diferentes. El propósito básico de anulación es el polimorfismo, pero no es posible en el ejemplo anterior

+1

Las firmas son las mismas aquí. Las funciones difieren solo en el tipo de devolución y el tipo de devolución no es parte de la firma. –

+0

@LinuxPenseur gracias por contar, eso es lo que quise decir – kvk

1

Si desea sobrescribir, intente utilizar la plantilla.

ver lo siguiente:

#include <iostream> 

using namespace std; 

class base 
{ 
    public: 
     template<typename X> X func() 
     { 
     cout << "vfunc in base class\n"; 
     return static_cast<X>(0); 
     } 
};  

class derived: public base 
{ 
    public: 
     template<typename X> X func() 
     { 
     cout << "vfunc in derived class\n"; 
     return static_cast<X>(2); 
     } 
};  

int main() 
{ 
    derived *bptr = new derived; 
    cout << bptr->func<int>() << endl; 
    cout << dynamic_cast<base*>(bptr)->func<int>() << endl; 

    derived *bptr2 = new derived; 
    cout << bptr->func<double>() << endl; 
    cout << dynamic_cast<base*>(bptr)->func<int>() << endl; 


    return 0; 
} 

Por supuesto, usted no necesita declarar en dos clases diferentes de esa manera, se podría hacer:

class base 
{ 
    public: 
     int func() 
     { 
     cout << "vfunc in base class\n"; 
     return 0; 
     } 

     double func(){ 
     cout << "vfunc for double class\n"; 
     return 2.; 

     } 
}; 
Cuestiones relacionadas