2011-06-24 10 views
32

Tengo una situación extraña con el siguiente código. Por favor, ayúdame a aclararlo.función virtual argumentos predeterminados comportamiento

class B 
{ 
     public: 
      B(); 
      virtual void print(int data=10) 
      { 
        cout << endl << "B--data=" << data; 
      } 
}; 
class D:public B 
{ 
     public: 
      D(); 
      void print(int data=20) 
      { 
        cout << endl << "D--data=" << data; 
      } 
}; 

int main() 
{ 
    B *bp = new D(); 
    bp->print(); 
return 0; 
} 

En cuanto a la salida que esperaba

[ D--data=20 ] 

Sin embargo, en la práctica es

[ D--data=10 ] 

Por favor, ayuda. Puede parecer obvio para ti, pero no conozco el mecanismo interno.

+3

Si una respuesta resuelve su problema (o hace que usted lo entienda), por favor, acepte que, utilizando la marca verde en la parte izquierda de la respuesta. –

Respuesta

22

Los argumentos predeterminados son completamente en tiempo de compilación. Es decir. la sustitución de argumentos predeterminados en lugar de argumentos perdidos se realiza en tiempo de compilación. Por esta razón, obviamente, no hay forma de que la selección de argumento predeterminada para las funciones miembro dependa del tipo dinámico (es decir, tiempo de ejecución) del objeto. Siempre depende de static (es decir, tiempo de compilación) tipo del objeto.

La llamada que escribió en su código de ejemplo es inmediatamente interpretada por el compilador como bp->print(10) independientemente de cualquier otra cosa.

+0

Gracias a Andrey por despejar mi duda. No pensé en esta dirección. Muchas gracias. –

30

La norma dice (8.3.6.10):

Una llamada de función virtual (10.3) utiliza los argumentos por omisión en la declaración de la función virtual determinado por el tipo estático del puntero o referencia que denota el objeto . Una función de anulación en una clase derivada no adquiere los argumentos predeterminados de la función que reemplaza.

Esto significa, ya que está llamando print través de un puntero de tipo B, utiliza el argumento predeterminado de B::print.

+0

Fundoo responde pero ayuda a entender. Gracias –

0

su variable es de tipo B, por lo que se llamará a la función de B. Para llamar a D, debe declarar su variable como D, o enviarla a D.

+0

Como la función del miembro 'print' se declara virtual, la llamada a través de un puntero de clase base llamará a la versión derivada de la función miembro, no a la versión base. – templatetypedef

1

El valor del argumento predeterminado se pasa en nombre de la persona que llama. Desde el punto de vista del llamante, funciona con clase B (no D), por lo que pasa 10 (como para la clase B)

3

Generalmente, se usan los argumentos predeterminados que son visibles en un determinado ámbito. Puede hacer (pero no debería) cosas originales:

#include <iostream> 
void frob (int x) { 
    std::cout << "frob(" << x << ")\n"; 
} 

void frob (int = 0); 
int main() { 
    frob();      // using 0 
    { 
     void frob (int x=5) ; 
     frob();     // using 5 
    } 
    { 
     void frob (int x=-5) ; 
     frob();     // using -5 
    } 
} 

En su caso, la firma de la clase base está visible. Para usar los argumentos por defecto derivados, debe llamar explícitamente a esa función a través de un puntero a su clase derivada, ya sea al declararlo de esa manera o al lanzarlo correctamente.

0

La vinculación dinámica utiliza vpointer y vtable. Sin embargo, el enlace dinámico solo se aplica al puntero de función. No hay ningún mecanismo para el argumento de vinculación dinámica.

Por lo tanto, el argumento predeterminado se determina estáticamente en el momento del compilador. En este caso, está estáticamente determinado por el tipo de bp, que es un puntero a la clase Base.Por lo tanto, data = 10 se pasa como argumento de la función, mientras que el puntero de la función apunta a la función del miembro de la clase derivada: D :: print. Esencialmente, llama a D :: print (10).

El siguiente fragmento de código y las salidas resultantes demuestran claramente el punto: aunque llama a la función Derived :: member (resize) del miembro de la llamada derivada, pasa el argumento predeterminado de la clase base: size = 0.

virtual void Derivado :: cambio de tamaño (int) tamaño 0

#include <iostream> 
#include <stdio.h> 
using namespace std; 

#define pr_dbgc(fmt,args...) \ 
    printf("%d %s " fmt "\n",__LINE__,__PRETTY_FUNCTION__, ##args); 

class Base { 
    public: 
     virtual void resize(int size=0){ 
      pr_dbgc("size %d",size); 
     } 
}; 

class Derived : public Base { 
    public: 
     void resize(int size=3){ 
      pr_dbgc("size %d",size); 
     } 
}; 

int main() 
{ 
    Base * base_p = new Base; 
    Derived * derived_p = new Derived; 

    base_p->resize();   /* calling base member function 
            resize with default 
            argument value --- size 0 */ 
    derived_p->resize();  /* calling derived member  
            function resize with default 
            argument default --- size 3 */ 

    base_p = derived_p;   /* dynamic binding using vpointer 
            and vtable */ 
           /* however, this dynamic binding only 
            applied to function pointer. 
            There is no mechanism to dynamic 
            binding argument. */ 
           /* So, the default argument is determined 
            statically by base_p type, 
            which is pointer to base class. Thus 
            size = 0 is passed as function 
            argument */ 

    base_p->resize();   /* polymorphism: calling derived class 
            member function 
            however with base member function 
            default value 0 --- size 0 */ 

    return 0; 
} 


#if 0 
The following shows the outputs: 
17 virtual void Base::resize(int) size 0 
24 virtual void Derived::resize(int) size 3 
24 virtual void Derived::resize(int) size 0 
#endif 
Cuestiones relacionadas