comportamento degli argomenti predefiniti della funzione virtuale

Ho una strana situazione sul seguente codice. Per favore aiutami a chiarirlo.

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=" <print(); return 0; } 

Per quanto riguarda l’uscita che mi aspettavo

 [ D--data=20 ] 

Ma in pratica lo è

 [ D--data=10 ] 

Per favore aiuto. Potrebbe sembrare ovvio per te, ma non sono a conoscenza del meccanismo interno.

Gli argomenti predefiniti sono funzionalità interamente in fase di compilazione. Vale a dire la sostituzione degli argomenti predefiniti al posto degli argomenti mancanti viene eseguita in fase di compilazione. Per questo motivo, ovviamente, non c’è modo che la selezione degli argomenti predefinita per le funzioni membro possa dipendere dal tipo dinamico (cioè di runtime) dell’object. Dipende sempre dal tipo statico (cioè in fase di compilazione) dell’object.

La chiamata che hai scritto nel tuo esempio di codice viene immediatamente interpretata dal compilatore come bp->print(10) indipendentemente da qualsiasi altra cosa.

Lo standard dice (8.3.6.10):

Una chiamata di funzione virtuale (10.3) utilizza gli argomenti predefiniti nella dichiarazione della funzione virtuale determinata dal tipo statico del puntatore o riferimento che indica l’object. Una funzione di override in una class derivata non acquisisce argomenti predefiniti dalla funzione che sovrascrive.

Ciò significa che, poiché si chiama la print tramite un puntatore di tipo B , viene utilizzato l’argomento predefinito di B::print .

In genere, vengono utilizzati quegli argomenti predefiniti che sono visibili in un determinato ambito. Puoi fare (ma non dovresti) cose funky:

 #include  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 } } 

Nel tuo caso, la firma della class base è visibile. Per utilizzare gli argomenti predefiniti derivati, è necessario chiamare esplicitamente tale funzione tramite un puntatore alla class derivata, dichiarandola in questo modo o eseguendo correttamente il casting.

Il valore dell’argomento predefinito viene inoltrato per conto del chiamante. Dal punto di vista del chiamante funziona con la class B (non D), quindi passa 10 (come per la class B)

la tua variabile è di tipo B, quindi verrà chiamata la funzione di B. Per chiamare D, devi dichiarare la tua variabile come D, o lanciare su D.

Il collegamento dinamico utilizza vpointer e vtable. Tuttavia, il collegamento dinamico si applica solo al puntatore di funzione. Non esiste un meccanismo per l’argomento di collegamento dinamico.

Pertanto, l’argomento predefinito viene determinato staticamente al momento del compilatore. In questo caso, è determinato staticamente dal tipo bp, che è un puntatore alla class Base. Quindi data = 10 viene passato come argomento della funzione, mentre il puntatore della funzione punta alla funzione membro della class Derived: D :: print. In sostanza, chiama D :: print (10).

Il seguente frammento di codice e gli output risultanti dimostrano chiaramente il punto: anche se chiama la funzione Derived call member Derived :: resize (int), passa l’argomento predefinito della class Base: size = 0.

virtual void Derived :: ridimensiona (int) dimensione 0

 #include  #include  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