2009-05-22 9 views
11

Est-ce que quelqu'un sait pourquoi cela donne une erreur de compilation? J'ai essayé VS 2005 et CodeWarrior:Référence aux pointeurs et au polymorphisme 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; 
} 

Visual Studio dit:

refptr.cpp (33): erreur C2664: 'RemoveObj': ne peut pas convertir le paramètre 1 de 'enfant *' à 'parent * &'

Merci

Répondre

16

Le paramètre ppObj à RemoveOb j est une référence à un parent *. Que faire si la méthode RemoveObj() a remplacé le pointeur par un pointeur vers un nouvel objet Parent? Lorsque la méthode a renvoyé votre, le pCObjChild* ne pointerait plus vers un objet Child.

+0

Beau raisonnement. +1 :) –

4

De la norme C++ (1998)

Sauf dans le cadre d'une initialisation par conversion défini par l'utilisateur (13.3.1.4, 13.3.1.5), une séquence conversion implicite bien formé est l'une des formes suivantes : -une séquence conversion standard (13.3.3.1.1) -a utilisateur défini ...

13.3.3.1.1

au plus une conversion de chaque catégorie est autorisée en une seule séquence de conversion standard

donc C++ ne peut pas convertir implicitement deux fois consécutives: pointeur vers un pointeur, puis à nouveau de pointeur .

au clair considérer cette déclaration du RemoveObj

void RemoveObj(Parent ** ppObj) 

Et vous verrez cette erreur

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

Vous devez utiliser la conversion explicite comme

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

ou ont changer

void RemoveObj(Parent *& ppObj) 

à

void RemoveObj(Parent * ppObj) 

ou

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

Cela ne répond pas pourquoi c'est une erreur complier, mais c'est ce qu'il devrait être changé pour être. La référence au pointeur n'est pas nécessaire pour faire ce que fait la méthode. –

+0

J'ai une référence additionnelle à la norme. –

+0

Avec l'édition, c'est certainement une réponse à la question de savoir pourquoi vous obtenez une erreur de compilation. Vous essayez une conversion en deux étapes qui est illégale selon la partie de la norme @MykolaGolubyev a souligné. Malheureusement, un 'reinterpret_cast' est la solution la plus pratique à ce problème. –

-2

Ce ne fait pas autorité, mais je crois que le problème est la nature polymorphique des classes C++ ne couvre pas leurs pointeurs; ce que vous attendez d'être fait ici est Child * pour être converti en Parent *; alors que vous pouvez convertir un Child en Parent, vous ne pouvez pas utiliser le pointeur référence. Autrement dit, les classes sont polymorphes, mais les pointeurs sur les classes ne sont pas considérés comme des références. C'est pour la raison que Michael Burr donne ci-dessus; le Child * implique une certaine structure de mémoire que le Parent * ne fait pas.

+3

conversion entre les pointeurs sont possibles. la conversion renvoie cependant une valeur temporaire. qui est la raison pour laquelle il ne peut pas être passé à une référence non-const. –

+1

Le problème provient de la référence, pas des pointeurs. Vous pouvez passer les pointeurs de manière interchangeable, mais pas une référence à un pointeur d'un pointeur de type différent. –

+2

Je pense que vous parliez de conversions de lvalues ​​en lvalues ​​w.r.t Child * to Parent *. Vous avez raison, une telle conversion n'est pas possible. Mais votre message semble vouloir dire que Child * to Parent * n'est pas autorisé (ce n'est pas le cas). N'a pas downvote, juste pour vous aider à comprendre pourquoi ils auraient pu vous downvoted. (si seulement ils vous l'ont dit ...) –

0

ppobj est la référence pour le pointeur. * ppobj dereferences ce que pointe la variable, donc vous obtenez la variable le pointeur.

Depuis le déréférencement n'est pas du type correct, vous voyez l'erreur.

0

Un pointeur sur une référence permet à la valeur du pointeur de changer dans la fonction. Comme indiqué par Michael Burr, il est possible d'affecter une référence de classe incorrecte et de la renvoyer. Imaginez votre programme entier incorrect en utilisant * pchickens comme * peggs :)

Je pensais qu'il valait la peine d'ajouter (mais pas explicitement ce que vous avez demandé): Ma préférence pour une implémentation polymorphique est de déplacer des fonctions communes à l'intérieur comme méthodes. Si tous partagent une fonction, ajoutez-la simplement à la classe de base.

Ensuite, de toute façon vous pouvez simplement appeler Foo-> Bar() et atteindre le résultat souhaité. Mais pour l'exemple d'implémentation spécifique que vous donnez, il suffit de supprimer Foo pour appeler le destructeur approprié.