2008-10-16 9 views
1

Cela peut ne pas être possible, mais j'ai pensé que je demanderais ...Suivi de la durée de vie variable automatique?

Y a-t-il un moyen de savoir si une variable automatique a été supprimée ou non sans modifier la classe de la variable elle-même? Par exemple, considérez ce code:

const char* pStringBuffer; 
{ 
    std::string sString("foo"); 
    pStringBuffer = sString.c_str(); 
} 

Il est évident que, après le bloc, pStringBuffer est un pointeur ballants qui peut ou peut ne pas être valide. Ce que je voudrais, c'est une façon d'avoir une classe wrapper qui contient pStringBuffer (avec un opérateur casting pour const char *), mais affirme que la variable référencée est toujours valide. En changeant le type de la variable référencée je peux certainement le faire (boost shared_ptr/weak_ptr, par exemple), mais j'aimerais pouvoir le faire sans imposer de restrictions sur le type référencé.

Quelques réflexions:

  • je vais probablement besoin de changer la syntaxe d'affectation pour inclure la variable référencée (ce qui est bien)
  • je pourrais être en mesure de regarder le pointeur de pile pour détecter si mon La classe wrapper a été allouée "plus tard" que la classe référencée, mais cela semble hacky et non standard (C++ ne définit pas le comportement de la pile). Cela pourrait fonctionner, cependant.

Pensées/solutions brillantes?

Répondre

0

Une technique peut vous être utile est de remplacer les opérateurs new/delete avec vos propres implémentations qui marquent les pages de mémoire utilisés (attribué par votre operator new) comme non accessibles lorsqu'ils sont libérés (désallouée par votre operator delete). Vous devrez vous assurer que les pages de mémoire ne sont jamais réutilisées, mais il y aura des limitations concernant la durée d'exécution en raison de l'épuisement de la mémoire.

Si votre application accède aux pages de mémoire une fois qu'elles ont été désallouées, comme dans votre exemple ci-dessus, le système d'exploitation piège l'accès tenté et déclenche une erreur. Ce n'est pas exactement le suivi en soi car l'application sera stoppée immédiatement mais elle fournit des informations :-)

Cette technique est applicable dans des scénarios étroits et n'attrape pas tous les types d'abus de mémoire mais elle peut être utile. J'espère que cela pourra aider.

1

En général, ce n'est simplement pas possible en C++, car les pointeurs sont trop "bruts". De plus, regarder si vous avez été affecté plus tard que la classe référencée ne fonctionnerait pas, car si vous changez la chaîne, alors le pointeur c_str pourrait bien changer.

Dans ce cas particulier, vous pouvez vérifier si la chaîne retourne toujours la même valeur pour c_str. Si c'est le cas, vous êtes probablement toujours valide et si ce n'est pas le cas, vous avez un pointeur invalide. En tant qu'outil de débogage, je vous conseille d'utiliser un système de suivi de mémoire avancé, comme valgrind (disponible seulement pour linux, j'ai peur.) Des programmes similaires existent pour windows mais je crois qu'ils coûtent tous de l'argent. J'ai linux installé sur mon mac). Au prix d'une exécution beaucoup plus lente de votre programme, valgrind détecte si vous avez déjà lu à partir d'un pointeur invalide. Bien que ce ne soit pas parfait, j'ai découvert qu'il détecte de nombreux bugs, en particulier ceux de ce type.

+0

Oui; Purify ferait des choses similaires, à l'époque où il a réellement fonctionné et n'a pas seulement corrompu votre application à l'exécution. Malheureusement, il s'agit d'une application d'exécution, qui coûte de l'argent et qui ne fonctionne pas dans les applications d'une complexité commerciale. = / – Nick

0

Vous pouvez créer une classe wrapper qui fonctionne dans le cas simple que vous avez mentionné.Peut-être quelque chose comme ceci:

X<const char*> pStringBuffer; 
{ 
    std::string sString("foo"); 
    Trick trick(pStringBuffer).set(sString.c_str()); 
} // trick goes out of scope; its destructor marks pStringBuffer as invalid 

Mais il ne permet pas des cas plus complexes:

X<const char*> pStringBuffer; 
{ 
    std::string sString("foo"); 
    { 
     Trick trick(pStringBuffer).set(sString.c_str()); 
    } // trick goes out of scope; its destructor marks pStringBuffer as invalid 
} 

Ici, le infirmation arrive trop tôt. Pour la plupart, vous devez simplement écrire un code aussi sûr que possible (voir: pointeurs intelligents), mais pas plus sûr (voir: performances, interfaces de bas niveau), et utiliser des outils (valgrind, Purify) pour vous assurer que rien ne glisse à travers les fissures.

0

Étant donné que "pStringBuffer" est la seule partie de votre exemple existant après que sString est hors de portée, vous avez besoin d'un changement, ou un substitut, qui reflète cela. Un mécanisme simple est une sorte de garde de portée, avec une portée correspondant à sString, qui affecte pStringBuffer quand il est détruit. Par exemple, il peut définir pStringBuffer sur NULL.

Pour ce faire, sans changer la classe de « la variable » ne peut être fait à bien des égards:

  • Introduire une variable distincte dans la même portée que sChaîne (pour réduire verbosité, vous pourriez envisager une macro pour générer les deux choses ensemble). Pas gentil.

  • Enveloppe avec un template ala X sString: on peut se demander s'il s'agit de "modifier le type de la variable" ... la perspective alternative est que sString devient un wrapper autour de la même variable. Il souffre également en ce que le mieux que vous puissiez faire est de faire passer les arguments passés par les constructeurs de templates aux constructeurs enveloppés jusqu'à des arguments N finis.

Aucune de ces solutions n'aide beaucoup car elles reposent sur le fait que le développeur se souvient de les utiliser.

Une meilleure approche beaucoup est de faire "const char * pStringBuffer" simplement "std :: string some_meaningful_name", et lui affecter au besoin. Compte tenu du décompte des références, ce n'est pas trop cher 99,99% du temps.

Questions connexes