2009-12-22 5 views
4

examiner un projet assez vieux, j'ai trouvé l'extrait de code curieux suivant (uniquement le code extrait pertinent):Que pourrait signifier cette curieuse combinaison de "while" et "delete"?

class CCuriousClass { 
    ~CCuriousClass(); 
    CSomeType* object; 
}; 

CCuriousClass::~CCuriousClass() 
{ 
    while(object != NULL) { 
     delete object; 
    } 
} 

Ai-je Supervisé quelque chose ou est-ce une route simple pour un comportement non défini?

Ce que je vois ici est que si object est un pointeur NULL au point de CCuriousClass::~CCuriousClass() étant appelé tout ira bien - aucune action prise - mais si object est nul pas que ce sera une boucle infinie avec un comportement non défini à l'intérieur.

Est-ce probablement un bug ou une construction intelligente que je ne comprends pas?

+0

Peut-être que quelque chose arrive à cet objet dans un thread différent? – Blindy

+1

Je ne sais pas mais pouvez-vous surcharger l'opérateur "delete" d'un objet? –

+1

@Mike, vous pouvez mais le destructeur d'objets sera toujours appelé. –

Répondre

6

Étant donné que votre question semble impliquer « Qu'est-ce que quelqu'un pourrait vouloir dire cela? » et non "Pourquoi est-ce une idée fantastique?" Je suggère ce qui suit:

class CSomeType { 
    CCuriousClass* m_plistentry; 
    CSomeType* m_pnext; 

    ~CSomeType() { 
     m_plistentry->object = m_pnext; 
    } 
}; 

L'idée de base aurait pu être que les points de propriétaire à la tête d'une liste, et la liste ne peuvent être supprimés à la tête. Si la tête est supprimée, elle définit son pointeur parent sur la nouvelle tête de la liste. Si le parent est détruit, il détruit tous les éléments de la liste.

Maintenant, c'est clairement le code de la ville folle.

+0

Il ne peut pas modifier le pointeur supprimé dans le code d'origine. –

+0

@Neil Je suis curieux, pourquoi ne peux-tu pas faire ça? –

+0

@Neil, ouais je me demande aussi. Cela me semble une explication valable –

13

Cela ressemble à un bug.

0

Le code semble bogué. Assurez-vous que destructeur ne devrait pas jeter des exceptions sinon il peut terminer le programme et il devrait être comme.

CCuriousClass::~CCuriousClass() 
{ 
    try 
    {  
      if(object != NULL) 
      { 
       delete object; 
       object = NULL; 
      }  
    } 
    catch(...) 
    { } 
} 
3

Comme vous le dites, c'est un bug. delete ne définit pas le pointeur qu'il supprime à NULL, ce que vous avez est une boucle infinie, qui peut être terminée ou non par le comportement indéfini que vous obtenez en supprimant deux fois le même pointeur.

1

Cela ressemble à un bogue, à moins que le destructeur de CSomeType puisse en quelque sorte modifier cet objet.

1

non seulement c'est probablement un bug mais c'est une vérification absolument inutile car la suppression d'un pointeur nul est bonne. remplacer par

CCuriousClass::~CCuriousClass() 
{  
    delete object;  
} 

ou mieux encore d'utiliser un pointeur intelligent et de se débarrasser complètement du destructeur.

9

Il est possible que certains fous aient implémenté CSomeType avec une référence arrière à leur CCuriousClass, et son destructeur crée parfois un remplacement. Quelque chose comme ceci:

class CSomeType 
{ 
public: 
    explicit CSomeType(CCuriousClass &parent) : parent(parent) {} 
    ~CSomeType() 
    { 
     parent.object = respawn() ? new CSomeType(parent) : 0; 
    } 
private: 
    CCuriousClass &parent; 
}; 

Je ne suggère pas que quiconque devrait jamais écrire une telle logique tordue. Il donne probablement encore un comportement indéfini, puisque je crois que delete est autorisé à modifier le pointeur. Mais cela pourrait expliquer pourquoi quelqu'un pourrait penser que le code donné pourrait être valide.

D'autre part, c'est probablement juste un bug causé par un malentendu sur la façon dont delete fonctionne.

+4

C'est magnifique! : p Je vais écrire tous mes destructeurs comme ça à partir de maintenant. – jalf

+0

respawn() devrait bien sûr retourner un bit aléatoire pour le vrai evilness –

+1

'delete' n'est pas autorisé à modifier son opérande (comme il ne doit même pas être un lvalue - considérer' delete new int'), donc il n'y a pas U.B. ici. Juste un pur mal. –

2

Ce comportement est possible, à condition d'une instance de CSomeType connaît l'adresse où un pointeur vers lui-même est stocké (CSomeType ** membre CSomeType) afin qu'il puisse remettre sur la suppression. Je ne sais pas pourquoi cela pourrait être nécessaire.

exemple:

struct self_know{ 
    self_know** pptr; 
    int cnt; 

    static self_know* create(self_know **_pptr){ 
     *_pptr = ::new self_know; 
     (*_pptr)->cnt = 10; 
     (*_pptr)->pptr = _pptr; 
     return *_pptr; 
    } 

    void operator delete(void*it){ 
     self_know *s = (self_know*)it; 
     if(--s->cnt<0){ 
     *(s->pptr)=0; 
     ::delete s; 
     } 

    } 
}; 

#include<iostream> 
main(){ 
    self_know *p = 0; 
    self_know::create(&p); 
    while(p != 0){ 
    std::cout << p->cnt << std::endl; 
    delete p; 
    } 
} 
1

Vérifiez la définition de classe CSomeType. Il peut y avoir une fonction "! =" Surchargée. Sinon, c'est clairement un bug.

2

Une autre théorie possible est que quelqu'un a joué un mauvais tour avec le préprocesseur. Dire:

struct delete_and_null { 
    template<class T> 
    delete_and_null& operator, (T*& p) { 
     delete p; 
     p = 0; 
     return *this; 
    } 
} delete_and_null; 

#define delete delete_and_null, 

Cela n'explique pas le besoin de la boucle, mais au moins cela éviterait U.B. et se terminer finalement.

0

Je suppose que c'est d'un programmeur C fou qui ne comprend pas les destructeurs et/ou la suppression de l'opérateur. Soit cela ou quelqu'un qui a maladroitement fait un 'while' au lieu d'un 'if' et ne savait pas que delete vérifie déjà null. Je ne perdrais pas de temps à spéculer sur le code fictif. La chose importante est de reconnaître que c'est idiot et fou.

Questions connexes