Manière générale de réinitialiser une variable membre à sa valeur d'origine en utilisant la pile?

StackOverflow https://stackoverflow.com/questions/207965

  •  03-07-2019
  •  | 
  •  

Question

Je suis tombé sur une fonction d'instance de classe qui devait modifier temporairement une variable d'instance de classe, puis la restaurer lorsque la fonction s'est terminée. La fonction avait des déclarations de retour un peu partout, et avant chaque retour, il y avait une déclaration de restauration. Cela me semblait chaotique, sans parler de la peur quand une exception est lancée.

Comme amélioration, j’ai proposé cette généralisation en utilisant une définition de classe interne. Voici un exemple de programme de 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 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

C’est ce que j’attends de "fin".

J'ai eu le sentiment que définir le restaurateur de classe dans la classe Unwind aide à décourager les utilisations abusives.

Ma question est la suivante: existe-t-il un moyen général et plus sûr de procéder? Je suis inquiet pour les problèmes de la vie.

Éditer: Veuillez supposer qu’il n’ya pas de threads, mais "en aval". méthodes sur la pile qui modifient le comportement en fonction de cet indicateur b_active_.

Était-ce utile?

La solution 4

J'ai révisé l'échantillon un peu plus en fonction des commentaires et placé comme réponse du wiki de la communauté 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

Autres conseils

Je suis d'accord avec Adam Pierce et pense également que vous devriez préférer les références aux pointeurs:

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

J'aime le modèle de restauration, mais je placerais probablement ce modèle en dehors de la classe Unwind ou même dans un fichier d'en-tête séparé afin de pouvoir le réutiliser ultérieurement. Cela le rendrait également un peu plus lisible.

Voici comment je le ferais aussi. Ainsi, si la fonction renvoie ou retourne tôt pour une raison quelconque, votre objet Restorer sera détruit et la variable réinitialisée à la valeur d'origine. La question qui se pose vraiment est la suivante: pourquoi faut-il avoir une variable qui est annulée lorsque la fonction revient? L'objet est-il utilisé à partir de plusieurs threads?

QuantumPete

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top