2011-11-26 7 views
7

Questo mi ha sorpreso un po ', ma stavo giocando con un po' di codice e ho scoperto che, almeno sul mio computer, quando una funzione accetta una classe genitrice per riferimento e si passa un'istanza child, che il problema di slicing non lo fa si verificano Per illustrare:Il passaggio per riferimento evita sempre il problema dell'affettatura?

#include <iostream> 

class Parent 
{ 
public: 
    virtual void doSomething() 
    { 
     using namespace std; 
     cout << "Parent::DoSomething" << endl; 
    } 
}; 

class Child : public Parent 
{ 
public: 
    virtual void doSomething() 
    { 
     using namespace std; 
     cout << "Child::DoSomething" << endl; 
    } 
}; 

void performSomething(Parent& parent) 
{ 
    parent.doSomething(); 
} 

int main(int argc, char** argv) 
{ 
    Child myChild; 

    performSomething(myChild); 

    return 0; 
} 

Questo stampa Child::DoSomething.

Come ho detto, sono rimasto un po 'sorpreso. Voglio dire, so che il passaggio per riferimento è come che passa i puntatori in giro (ma molto più sicuro per la mia comprensione), ma non sapevo che avrei comunque dovuto mantenere la bontà polimorfica nel farlo.

Voglio solo assicurarmi, è questo dovrebbe succedere o è uno di quelli tipo "funziona sulla mia macchina" di istanze?

+0

Questo non è ciò che "slicing" è. Inoltre, questo codice è compilabile? Che cosa è 'using std;'? –

+0

Significa che deve essere 'using namespace std'. Ho pensato che ero in grado di ottenere il punto attraverso, ma ripulito il codice per il beneficio complessivo del sito. – Anthony

risposta

9

Il comportamento visualizzato è corretto. Questo è come dovrebbe funzionare. I riferimenti funzionano esattamente come i puntatori.

+0

Grazie, è quello che ho pensato. Come ho detto, volevo solo un controllo di sanità mentale, quindi non ho fatto ipotesi non valide. – Anthony

2

Sì, il collegamento a un riferimento consente il collegamento dinamico. Questo è causato dalla differenza tra il tipo dinamico di un oggetto e il suo tipo statico.

Se si assume il parametro in base al valore, diventa una classe Parent. Sebbene se passi qualcosa attraverso un riferimento o un puntatore e chiami una funzione virtuale, il run-time cercherà lo dynamic type o most-derived type dell'oggetto reale a cui si fa riferimento.

3

Questo dovrebbe succedere. Passare per riferimento è ESATTAMENTE come i puntatori di passaggio: sta facendo la stessa cosa sotto il cofano. Non c'è magia per questo; ad ogni istanza di un oggetto polimorfico è associata una tabella di funzioni virtuale. Finché non copi nulla, non perderai le informazioni e le chiamate alle funzioni virtuali funzioneranno nel modo previsto.

Il motivo per cui si verificano problemi durante il passaggio per valore è che utilizzerà il costruttore di copie del tipo specificato nella firma della funzione, così si finirà con un'istanza completamente nuova della superclasse.

11

"Slicing" fa riferimento all'incapacità del costruttore della copia di base di distinguere le corrispondenze esatte dei tipi dalle classi derivate. L'unico modo per richiamare l'slicing è invocare il costruttore della copia di base. In genere questo si verifica quando il passaggio di argomenti per valore, anche se altre situazioni possono essere artificiosa:

class Base { }; 
class Derived : public Base { }; 

void foo(Base); 

int main() 
{ 
    Derived x; 

    Base y = x; // flagrant slicing 
    foo(x);  // slicing by passing by value 
} 

Non si è mai fare una cosa del genere, quindi non si verifichino situazioni di affettamento.

+2

Indicare il costruttore di copie quando si passa per valore è ciò che rende perfetta questa risposta. – dani