2009-04-10 12 views
6

stavo cercando di rispondere alla domanda menzionato here passando il riferimento al puntatore invece di puntatore a puntatore in questo modo:Conversione da * Derivato a base * &

class Parent 
{ 
}; 

class Child : public Parent 
{ 
}; 

void RemoveObj(Parent*& pObj) 
{ 
    delete pObj; 
    pObj = NULL; 
} 

int main() 
{ 
    Parent* pPObj = new Parent; 
    Child* pCObj = new Child; 
    pPObj = new Parent(); 
    pCObj = new Child(); 



    RemoveObj(pPObj); 
    RemoveObj(pCObj); // This is line 32 
    return 1; 
} 

Ma questo produce il seguente errore del compilatore a linea 32:

errore C2664: 'RemoveObj': non può convertire il parametro 1 da 'Bambino *' per 'Parent * &'

Sono d'accordo che la conversione da Child ** a Parent ** non è consentita. Ma perché questa conversione non è consentita?

+0

Perché restituire 1; alla fine di main()? Non dovrebbe essere restituito 0; ? – DeadHead

+0

Ho appena copiato il codice dalla domanda collegata..ma quello non è correlato alla domanda – Naveen

+0

Oh, so che non era realmente correlato alla domanda. La semplice curiosità combinata con il pensiero potrebbe esserci stato un vuoto nella mia conoscenza. – DeadHead

risposta

7

Un oggetto di tipo Child* non può essere associato a un Parent*& esattamente per lo stesso motivo per cui un Child** non può essere convertito in un Parent**. Permetterebbe al programmatore (intenzionalmente o meno) di rompere la sicurezza del tipo senza un cast.

class Animal {}; 

class DangerousShark : public Animal {}; 

class CuteKitten : public Animal {}; 

void f(Animal*& animalPtrRef, Animal* anotherAnimalPtr) 
{ 
    animalPtrRef = anotherAnimalPtr; 
} 

void g() 
{ 
    DangerousShark myPet; 
    CuteKitten* harmlessPetPtr; 

    f(harmlessPetPtr, &myPet); // Fortunately, an illegal function call. 
} 

Modifica

ritengo che alcune delle confusione nasce a causa dell'uso sciolto delle parole 'convert' e 'conversione'.

I riferimenti non possono essere rimbalzi, a differenza degli oggetti che possono essere riassegnati, quindi nel contesto dei riferimenti quando parliamo di conversione possiamo solo preoccuparci dell'inizializzazione di un nuovo riferimento.

I riferimenti sono sempre legati a un oggetto e dalla domanda dell'OP era chiaro che il suo obiettivo è ottenere un riferimento che è un vincolo diretto a un oggetto esistente. Questo è consentito solo se l'oggetto utilizzato per inizializzare il riferimento è compatibile con riferimento con il tipo di riferimento. Essenzialmente, questo è solo se i tipi sono uguali, o il tipo dell'oggetto è derivato dal tipo di riferimento e il tipo di riferimento è almeno come qualificato cv come oggetto di inizializzazione. In particolare, i puntatori a tipi diversi non sono compatibili con i riferimenti, indipendentemente dalla relazione dei tipi puntati.

In altri casi, un riferimento può essere inizializzato con qualcosa che può essere convertito nel tipo di riferimento. In questi casi, tuttavia, il riferimento deve essere const e non volatile e la conversione creerà un riferimento temporaneo e il riferimento sarà legato a questo oggetto temporaneo e non all'oggetto originale. Come sottolineato, questo non è adatto per i requisiti dell'esempio motivante dell'OP.

In sintesi, un Child può essere legato direttamente ad un Parent& ma un Child* non può essere direttamente legato a una Parent*&. A Parent* const& può essere inizializzato con un Child*, ma il riferimento verrà effettivamente associato a una copia temporanea dell'oggetto Parent* inizializzata dall'oggetto Child*.

+0

Grazie per l'esempio, ora è chiaro. – Naveen

+0

@Charles Bailey: alcuni problemi: [1] Il dtor corretto non verrà chiamato per gli oggetti di CuteKitten poiché il dtor per Animal non è virtuale. [2] innoclessPtr non è definito. – dirkgently

+0

Grazie, ho cambiato innocuo per innocuoPet all'ultimo momento, ma solo in un unico luogo. Nel mio esempio non c'è nessuna cancellazione quindi non sono sicuro di come si applica il tuo punto [1]. –

4
  • Le tue classi non hanno una funzione virtuale. Vedi FAQ 20.7

  • Beacuse Parent *& è un riferimento a un puntatore a un oggetto Parent. Stai passando un puntatore a un Child - questi sono tipi incompatibili. È possibile associare un temporaneo a un const riferimento vale a dire se si cambia il parametro:

    void RemoveObj(Parent* const& foo);

Ma allora non sarà in grado di fare molto con questo.

Era solo un codice di prova, quindi non ho creato alcun distruttore virtuale. Se capisco correttamente nella seconda chiamata di RemoveObj(), ottengo un oggetto Parent * temporaneo che può essere passato come riferimento const alla funzione. È corretto?

vi consiglio caldamente di eseguire il seguente programma in modalità standard C++ 98, una volta come in e poi di nuovo dopo aver commentato foo(b) e commentata delete b. Quindi, prova a inserire virtual prima del ~s(). Le differenze dovrebbero essere auto-esplicative!

#include <iostream> 
using namespace std; 
struct s { 
    s() {cout << __func__ << endl; } 
    ~s() {cout << __func__ << endl; } 
}; 

struct t : s { 
    t() {cout << __func__ << endl; } 
    ~t() {cout << __func__ << endl; } 
}; 

void foo(s* const& x) { delete x; } 

int main() { 
t* b = new t; 
foo(b); 
//delete b; 
} 
+0

Era solo un codice di test, quindi non ho creato alcun distruttore virtuale. Se ho capito correttamente nella seconda chiamata di RemoveObj(), ottengo un oggetto Parent * temporaneo che può essere passato come riferimento const alla funzione. È corretto? – Naveen

0

tipo * & è un'altra forma sintattica di tipo ** e Parent * & Bambino * & sono slegati tra loro così come genitore ** ** Bambino - questi sono diversi tipi non in una classe gerarchia.

0

Questo non funzionerà per le ragioni menzionate da Dirk. Se davvero bisogno di un metodo RemoveObj poi vorrei solo salvare il vostro oggetto Bambino appena allocata come genitore *:

#include <iostream> 

class Parent 
{ 
public: 
    virtual ~Parent() 
    { 
     std::cout << "Parent destructor" << std::endl; 
    } 
}; 

class Child : public Parent 
{ 
public: 
    virtual ~Child() 
    { 
     std::cout << "Child destructor" << std::endl; 
    } 
}; 

void RemoveObj(Parent*& pObj) 
{ 
    delete pObj; 
    pObj = NULL; 
} 



int main (int argc, const char * argv[]) { 

    Parent* pPObj = new Parent; 
    Parent* pCObj = new Child; 

    RemoveObj(pPObj);  
    RemoveObj(pCObj); // This is line 32 


    return 0; 
} 
+0

Sì, eseguendolo a mano (nessuna conversione implicita) funziona. –

1

è possibile convertire un Child* to a Parent*: questo crea un temporaneo. Ma non puoi associare un riferimento non const a quel temporaneo.

Questo non è un problema di **/*&/etc. Quello che stai cercando di fare è totalmente OK e ha senso. Quello squalo contro gattino ha lo stesso problema: non si tratta di mescolare gattino e squali. Non è possibile associare un riferimento non const a quel puntatore senza nome.

non è questo il problema Parent** vs. Child**: lì, se un Child** era un Parent**, allora si potrebbe assegnare p[0] = new NotAChild;. Una raccolta di oggetti che sono tutti sottotipi di A non è una raccolta di A.