2009-05-22 13 views
11

weiß jemand, warum dies einen Compilerfehler gibt? Ich habe versucht, VS 2005 und Codewarrior:Referenz auf Zeiger und C++ Polymorphismus

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; 
} 

Visual Studio sagt:

refptr.cpp (33): Fehler C2664: 'RemoveObj': kann nicht Parameter 1 aus konvertieren 'Child *' zu 'Eltern * &'

Dank

Antwort

16

Der ppObj Parameter RemoveOb j ist eine Referenz auf ein Elternteil *. Was passiert, wenn die Methode RemoveObj() den Zeiger durch einen Zeiger auf ein neues Objekt Parent ersetzt? Wenn die Methode zurückgegeben wurde, würde die pCObjChild* nicht mehr auf ein Child Objekt zeigen.

+0

Schöne Argumentation. +1 :) –

4

Aus der C++ Standard (1998)

Außer im Rahmen einer Initialisierung durch benutzerdefinierte Umwandlung (13.3.1.4, 13.3.1.5), eine wohlgeformte implizite Umwandlung Sequenz ist eine der folgenden Formen : -a Standardkonvertierung Sequenz (13.3.3.1.1) -a user definiert ...

13.3.3.1.1

Höchstens eine Umwandlung von jeder Kategorie wird in einer einzigen Standardkonvertierungssequenz erlaubt

So C++ nicht implizit zwei Mal in Folge umwandeln kann: Zeiger auf Zeiger und dann wieder von Zeigern .

löschen diese auf eine solche Erklärung des RemoveObj

void RemoveObj(Parent ** ppObj) 

betrachten und Sie werden diese Fehler

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

Sie haben sehen explizite Konvertierung zu verwenden, wie

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

oder haben zu ändern

void RemoveObj(Parent *& ppObj) 

zu

void RemoveObj(Parent * ppObj) 

oder

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

Dies beantwortet nicht, warum es ein Compiler-Fehler ist, aber es ist, was es geändert werden sollte. Der Verweis auf den Zeiger ist nicht erforderlich, um das zu tun, was die Methode ausführt. –

+0

Ich habe Adder Verweis auf den Standard. –

+0

Mit der Bearbeitung ist dies definitiv eine Antwort, warum Sie einen Compilerfehler bekommen. Sie versuchen eine zweistufige Konvertierung, die gemäß dem Teil des Standards @MykolaGolubyev ungültig ist, wies darauf hin. Leider ist ein 'reininterpret_cast' die praktischste Lösung für dieses Problem. –

-2

Dies ist nicht maßgebend, aber ich glaube, das Problem ist die polymorphe Natur von C++ Klassen ist nicht auf ihre Zeiger erstrecken; was Sie hier erwarten, ist für eine Child * zu einem Parent * gegossen werden; Während Sie eine Child auf eine Parent werfen können, können Sie nicht den Zeiger Referenz werfen. Das heißt, die Klassen sind polymorph, aber die Zeiger auf die Klassen werden nicht als Referenzen verwendet. Dies ist aus dem Grund, dass Michael Burr oben gibt; Die Child * impliziert eine bestimmte Speicherstruktur, die die Parent * nicht tut.

+3

Konvertierung zwischen Zeigern möglich. Die Konvertierung gibt jedoch einen rvalue temporary zurück. Aus diesem Grund kann es nicht an eine nicht konstante Referenz übergeben werden. –

+1

Das Problem ist von der Referenz, nicht die Zeiger. Sie können die Zeiger synonym übergeben, aber keine Referenz auf einen Zeiger eines anderen Typs. –

+2

Ich glaube, Sie sprachen über Konvertierungen von lvalues ​​zu lvalues ​​w.r.t Child * to Parent *. Du hast Recht, eine solche Umwandlung ist nicht möglich. Aber Ihr Beitrag klingt wie Sie implizieren, dass Child * to Parent * nicht erlaubt ist (ist es nicht). Habe nicht runtergestimmt und dir geholfen, herauszufinden, warum sie dich hätte runterziehen können. (wenn sie dir nur gesagt haben ...) –

0

ppobj ist die Referenz für den Zeiger. * ppobj dereferenziert, worauf die Variable zeigt, so erhalten Sie die Variable den Zeiger.

Da die Dereferenzierung nicht vom richtigen Typ ist, wird der Fehler angezeigt.

0

Ein Zeiger auf eine Referenz ermöglicht die Änderung des Zeigerwerts in der Funktion. Wie von Michael Burr festgestellt, bestünde die Möglichkeit, eine falsche Klassenreferenz zuzuweisen und sie zurückzugeben. Stellen Sie sich Ihr gesamtes Programm fälschlicherweise mit * pchickens als * peggs vor :)

Ich dachte, es hat sich gelohnt hinzuzufügen (obwohl nicht explizit, was Sie gefragt): Meine Präferenz für eine polymorphe Implementierung ist es, gemeinsame Funktionen als Methoden zu verschieben. Wenn sie alle eine Funktion teilen, fügen Sie einfach die Basisklasse hinzu.

Dann können Sie einfach Foo-> Bar() aufrufen und das gewünschte Ergebnis erzielen. Aber für das spezifische Implementierungsbeispiel, das Sie geben, einfach löschen Foo würde den entsprechenden Destruktor aufrufen.