¿Manera general de restablecer una variable miembro a su valor original usando la pila?

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

  •  03-07-2019
  •  | 
  •  

Pregunta

Encontré una función de instancia de clase que necesitaba cambiar temporalmente una variable de instancia de clase, y luego la restauré cuando la función se completara. La función tenía declaraciones de devolución en todo el lugar, y antes de cada devolución había una declaración de restauración. Eso me pareció complicado, sin mencionar el miedo cuando se lanza una excepción.

Como mejora, se me ocurrió esta generalización utilizando una definición de clase interna. Aquí hay un programa de controlador de muestra (restaurador de clase).

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 salida con g ++ 4.2.3 (-Wall) fue:

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

Que es lo que espero en " b end " ;.

Sentí que definir al restaurador de la clase dentro de la clase Desenrollar ayuda a desalentar el mal uso.

Mi pregunta es, ¿existe una forma general y más segura de hacer esto? Estoy preocupado por los problemas de por vida.

Editar: suponga que no hay subprocesos, pero " descendente " Métodos en la pila que cambian el comportamiento en función de esta bandera b_active_.

¿Fue útil?

Solución 4

Revisé la muestra un poco más en función de los comentarios y la coloqué como una respuesta de Community Wiki en lugar de editar la pregunta.

/// 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

Otros consejos

Estoy de acuerdo con Adam Pierce y también creo que debería preferir las referencias a los punteros:

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

Me gusta la plantilla del restaurador, pero probablemente coloque la plantilla fuera de la clase Unwind o incluso en un archivo de encabezado separado para que otras clases puedan reutilizarla en el futuro. Eso también lo haría un poco más legible.

Así es como lo haría yo también. De esta manera, si la función arroja o regresa temprano por alguna razón, su objeto Restaurador se destruirá y la variable se restablecerá al valor original. La pregunta realmente es, ¿por qué necesita una variable que se revierte cuando la función regresa? ¿Se utiliza el objeto de más de un hilo?

QuantumPete

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top