2009-05-22 23 views
11

qualcuno sa perché questo dà un errore del compilatore? Ho provato VS 2005 e CodeWarrior:Riferimento a puntatori e polimorfismo C++

class Parent { 
    protected: 
     int m_Var; 
    public: 
     Parent() : m_Var(0) {} 
     virtual ~Parent() {} 
     void PubFunc(); 
}; 

class Child : public Parent { 
    protected: 
     bool m_Bool; 
    public: 
     Child() : m_Bool(false) {} 
     virtual ~Child() {} 
     void ChildFunc(); 
}; 

void RemoveObj(Parent *& ppObj) 
{ 
    delete ppObj; 
    ppObj = 0; 
} 

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

    RemoveObj(pPObj); 
    RemoveObj(pCObj); 
    return 1; 
} 

studio visivo dice:

refptr.cpp (33): l'errore C2664: 'RemoveObj': non può convertire il parametro 1 da 'Bambino *' per 'Parent * &'

Grazie

risposta

16

Il parametro ppObj a RemoveOb j è un riferimento a un genitore *. Cosa accadrebbe se il metodo RemoveObj() sostituisse il puntatore con un puntatore a un nuovo oggetto Parent? Quando il metodo è stato restituito, lo pCObjChild* non punta più a un oggetto Child.

+0

Bel ragionamento. +1 :) –

4

Dalla standard C++ (1998)

Tranne nel contesto di un inizializzazione definito dall'utente conversione (13.3.1.4, 13.3.1.5), un ben formato sequenza conversione implicita è una delle seguenti forme : -a norma sequenza conversione (13.3.3.1.1), l'utente -a definito ...

13.3.3.1.1

al massimo una conversione di ogni categoria è consentito in un unico normale sequenza di conversione

Quindi C++ non può convertire implicitamente due volte di fila: dal puntatore al puntatore e poi di nuovo da un puntatore .

Per chiarire la situazione in considerazione tale dichiarazione del RemoveObj

void RemoveObj(Parent ** ppObj) 

e vedrete questo errore

error: invalid conversion from 'Child**' to 'Parent**' 

è necessario utilizzare la conversione esplicita come

RemoveObj((Parent**)&pCObj); 
    RemoveObj((Parent*&)&pCObj); 

o hanno cambiare

void RemoveObj(Parent *& ppObj) 

a

void RemoveObj(Parent * ppObj) 

o per

template <typename T> 
void RemoveObj(T *& pObj) 
{ 
    delete pObj; 
    pObj = 0; 
} 
+0

Questo non risponde perché è un errore del compilatore, ma è quello che dovrebbe essere cambiato per essere. Il riferimento al puntatore non è necessario per fare ciò che sta facendo il metodo. –

+0

Ho un riferimento ad Adder allo standard. –

+0

Con la modifica, questa è sicuramente una risposta al motivo per cui stai ricevendo un errore del compilatore. Stai tentando una conversione in due passaggi che è illegale in base alla parte dello standard che @MykolaGolubyev ha sottolineato. Sfortunatamente, un 'reinterpret_cast' è la soluzione più pratica a questo problema. –

-2

Questo non è autorevole, ma credo che il problema è la natura polimorfica di classi C++ non si estende ai loro puntatori; quello che ci si aspetta di fare qui è per un Child * da trasmettere a un Parent *; mentre è possibile trasmettere un Child a un Parent, non è possibile eseguire il cast del puntatore riferimento. Cioè, le classi sono polimorfiche, ma i puntatori alle classi non vengono presi come riferimenti. Questo è il motivo per cui Michael Burr dà sopra; il Child * implica una certa struttura di memoria che lo Parent * non ha.

+3

è possibile la conversione tra i puntatori. la conversione tuttavia restituisce un valore provvisorio. che è la ragione per cui non può essere passato a un riferimento non const. –

+1

Il problema è dal riferimento, non dai puntatori. È possibile passare i puntatori in modo intercambiabile, ma non un riferimento a un puntatore di un puntatore di tipo diverso. –

+2

Penso che stavate parlando di conversioni di lvalue su lvalues ​​w.r.t Child * su Parent *. Hai ragione, una tale conversione non è possibile. Ma il tuo post sembra che tu stia insinuando che Child * to Parent * non è consentito (non lo è). Non è stato un downvote, ma ti aiuta a capire perché potrebbero averti svalutato. (se ti dicessero solo ...) –

0

ppobj è il riferimento per il puntatore. * ppobj dereferences a cosa punta la variabile, in modo da ottenere la variabile il puntatore.

Poiché il dereferenziazione non è del tipo corretto si vede l'errore.

0

Un puntatore a un riferimento consente al valore del puntatore di cambiare nella funzione. Come notato da Michael Burr ci sarebbe la possibilità di assegnare un riferimento di classe errato e restituirlo. Immagina che l'intero programma usi erroneamente * pchickens as * peggs :)

Ho pensato che valesse la pena aggiungere (anche se non esplicitamente quello che hai chiesto): La mia preferenza per un'implementazione polimorfica è quella di spostare le funzioni comuni all'interno come metodi. Se tutti condividono una funzione, aggiungila semplicemente alla classe base.

Quindi in entrambi i casi è sufficiente chiamare Foo-> Barra() e ottenere il risultato desiderato. Ma per l'esempio di implementazione specifico che fornisci, semplicemente elimina Foo chiamerebbe il distruttore appropriato.