2009-04-10 9 views
2

Je recherche un moyen approprié de nettoyer mes pointeurs. Voici l'exemple de code:Le polymorphisme C++ n'est pas pris en charge pour le pointeur vers le pointeur

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) 
{ 
    *ppObj->PubFunc(); 
    delete *ppObj; 
    ppObj = NULL; 
} 

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

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

Mais le compilateur donne l'erreur:

classes.cpp:33: error: invalid conversion from ‘Child**’ to ‘Parent**’ 
classes.cpp:33: error: initializing argument 1 of ‘void RemoveObj(Parent**)’ 

Répondre

12

Il y a soo beaucoup de façons de gérer correctement la mémoire.

L'un près de votre exemple serait:

template <typename T> 
RemoveObj(T **p) 
{ 
    if (p == NULL) return; 
    delete *p; 
    *p = NULL; 
} 

De plus, vous voudrez peut-être utiliser std :: auto_ptr à la place. Il ressemblerait à ceci:

int main() 
{ 
    std::auto_ptr<Parent*> pPObj(new Parent); 
    std::auto_ptr<Child*> pCObj(new Child); 
    // no deletes needed anymore 
+0

A propos de la première solution: Quelle est l'amélioration de la comparaison: vide RemoveObj (void ** ppObj) – To1ne

+0

La première suggestion compile et travaux. void RemoveObj (void ** ppObj) peut être défini mais si vous essayez de l'appeler, votre appel ne devrait pas être compilé. –

+0

@ To1ne: Je pense que la suppression de void * est en fait indéfinie. Avec la solution de modèle, vous avez un type statique correct. Quel destructeur non trivial devrait être appelé pour un vide *? –

1

Vous pouvez trouver des informations utiles dans le livre < commun de connaissances C de> Article 8. Pointeurs à Pointeurs.

2

Vous n'avez pas besoin d'une enveloppe pour supprimer, rester simple:

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

    delete pPObj; 
    delete pCObj; // This is line 33 
    return 1; 
} 

Et rappelez-vous rencontrer des problèmes de supprimer des objets de type tableau avec votre RemoveObj (puisque vous utilisez toujours un delete scalaire). Une alternative est bien sûr de passer un drapeau pour indiquer que vous voulez delete []. Mais comme je l'ai dit: KISS.

+0

Peut-être que mon exemple était trop simple ... Je voulais appeler PubFunc() dans cette fonction aussi ... – To1ne

3

Pour faire simple:

enfant est une sous-classe de parent ce qui signifie que l'enfant * peut être substitué par un parent *

MAIS

enfant * n'est pas une sous-classe de parent * si cela signifie que Child ** ne peut pas être remplacé par Parent **

"Enfant" et "Enfant *" ne sont pas du même type.

2

Si votre problème concerne la mémoire et les ressources, le meilleur conseil serait d'oublier complètement votre approche et d'utiliser des pointeurs intelligents. std :: auto_ptr ou boost :: shared_ptr serait un point de départ.

Si vous détenez toutes vos ressources allouées par tas avec des pointeurs intelligents, votre code sera plus robuste.

3

Ce que vous devez faire est d'annuler tous les pointeurs vers l'objet que vous venez de supprimer. L'idée des pointeurs est qu'il y aura plus d'un pointeur stockant l'adresse du même objet. Si ce n'est pas le cas, il y a peu de raison d'utiliser un pointeur nu, et donc le motif que vous essayez de capturer n'est pas très utile - mais vous êtes loin de la première personne à l'essayer. Comme d'autres réponses l'ont mentionné, la seule façon de traiter les pointeurs est de contrôler soigneusement l'accès à ceux-ci.

Le titre de votre question est correct! Il y a une bonne raison à cela. Un pointeur identifie un emplacement qui stocke un objet d'un type spécifique. Un pointeur sur un pointeur vous permet de changer l'objet pointé par un pointeur.

void Foo(Parent **pp) 
{ 
    *pp = new OtherChild(); 
} 

Votre Child classe dérive de Parent, et le fait de ma classe OtherChild. Supposons que le compilateur vous a permis de le faire:

Child *c = 0; 
Foo(&c); 

Vous attendre à ce que le travail, mais si elle avait, nous aurions maintenant un pointeur Childc que dans les pointeurs de fait à une instance de OtherChild. Qui a dit que ces deux types sont compatibles?

Encore une fois, c'est un malentendu très fréquent - il revient à plusieurs reprises ici pour d'autres langues, en particulier en ce qui concerne List<Parent> et List<Child> en C#.

+0

Merci beaucoup, super explication !! – To1ne

+0

Pas de problème! Et rappelez-vous - rien ne dit "merci" comme une mise à jour. –

0

probablement la solution la plus simple que j'ai trouvé:

#define __REMOVE_OBJ(pObj) RemoveObj(pObj); pObj = NULL; 

Et il suffit d'appeler celui-ci:

__REMOVE_OBJ(pPObj); 
    __REMOVE_OBJ(pCObj); 

Mais je ne aime pas vraiment moi-même ...

0

De discussion sur make shared_ptr not use delete

Le pointeur partagé vous assure le nettoyage lorsque vous devriez et que vous n'accédez pas à quelque chose qui est détruit. En outre, vous pouvez vous spécialiser et fournir une méthode de destruction alternative.

boost::shared_ptr<T> ptr(new T, std::mem_fun_ref(&T::deleteMe)); 
Questions connexes