2008-10-16 13 views
2

Je suis tombé sur une fonction d'instance de classe qui avait besoin de changer temporairement une variable d'instance de classe, puis de la restaurer lorsque la fonction était terminée. La fonction avait des instructions de retour partout, et avant chaque retour il y avait une déclaration de restauration. Cela m'a semblé désordonné, sans parler de l'effrayant quand une exception est levée.Manière générale de réinitialiser une variable membre à sa valeur d'origine en utilisant la pile?

Comme une amélioration je suis venu avec cette généralisation en utilisant une définition de classe interne. Voici un exemple de programme pilote (restaurateur de classe).

class Unwind { 
private: 
    bool b_active_; ///< the thing I want to be restored 
    template<typename T> 
    class restorer { 
    T* ref_; 
    T save_; 
    public: 
    restorer(T* perm) : ref_(perm), save_(*ref_) {}; 
    ~restorer() { *ref_ = save_; } 
    }; 
public: 
    Unwind() : b_active_(false) {}; 
    void a() { out("a in"); b(); out("a end"); } 
    void b() { 
    out("b in"); 
    { 
     restorer<bool> trust_in_the_stack(&b_active_); // "restorer" created on the stack 
     b_active_ = true; // change b_active_ only while "within" b() 
     c(); 
     out("b inner end"); 
    } 
    out("b end"); 
    } 
    void c() { out("c in"); d(); out("c end"); } 
    void d() { out("d in"); cout << "deepest" << endl; out("d end"); } 
    void out(const std::string& msg) { 
    std::cout << msg << ": " << b_active_ << std::endl; 
    } 
}; 

int main() { Unwind u; u.a(); return 0; } 

La sortie en utilisant g ++ 4.2.3 (-Wall) était:

 
a in: 0 
b in: 0 
c in: 1 
d in: 1 
deepest 
d end: 1 
c end: 1 
b inner end: 1 
b end: 0 
a end: 0 

Ce qui est ce que je pense à "fin b". Je pensais que définir le restaurateur de classe à l'intérieur de la classe Unwind aide à décourager les abus.

Ma question est la suivante: existe-t-il une façon générale et plus sûre de le faire? Je suis inquiet pour les problèmes de la vie. Editer: Veuillez supposer qu'il n'y a pas de threads, mais des méthodes "en aval" sur la pile qui modifient le comportement basé sur cet indicateur b_active_.

+0

Je pense qu'il vaudrait mieux remplacer save _ (* ref_) dans la liste d'initialisation avec save _ (* perm). save _ (* ref_) peut vous mordre un jour, quand quelqu'un change l'ordre des déclarations dans votre classe. –

+0

@Maciej H: Bon point, merci! – piyo

Répondre

0

J'ai révisé l'échantillon un peu plus sur la base des commentaires et placé comme un Wiki communautaire répondre au lieu de modifier la question.

/// c++ code sample 
#ifndef UTIL_RESTORER_HPP 
#define UTIL_RESTORER_HPP 

namespace Utility { 

/// A Restorer instance ("inst") uses the stack to restore a saved 
/// value to the named variable when the instance "inst" goes out of 
/// scope. 
/// 
/// Restorer is designed to be an auto variable, not allocated on any 
/// other memory resource like a heap or in-place. 
template<typename T> 
class restorer { 
    T& ref_; 
    T save_; 
public: 
    restorer(T& perm) : ref_(perm), save_(perm) {} 
    ~restorer() { ref_ = save_; } 
}; 

}//NAMESPACE 
#endif//UTIL_RESTORER_HPP 
0

C'est comme ça que je le ferais aussi. De cette façon, si la fonction se déclenche, ou retourne tôt pour une raison quelconque, votre objet Restaurateur sera détruit et la variable réinitialisée à la valeur d'origine. La question est vraiment, pourquoi avez-vous besoin d'avoir une variable qui est inversée lorsque la fonction revient? L'objet est-il utilisé à partir de plusieurs threads?

QuantumPete

3

J'aime le modèle rénovateur mais je probablement mettre le modèle en dehors de la classe Déroulez ou même dans un fichier d'en-tête séparée de sorte qu'il peut être réutilisé par d'autres classes dans l'avenir. Cela le rendrait également un peu plus lisible.

6

Je suis d'accord avec Adam Pierce et pense aussi que vous devriez préférer des références sur des pointeurs:

template<typename T> 
class restorer { 
    T& ref_; 
    T save_; 
public: 
    restorer(T& perm) : ref_(perm), save_(ref_) {}; 
    ~restorer() { ref_ = save_; } 
};