2

我遇到了一个类实例函数,它需要临时更改一个类实例变量,然后在函数完成时恢复它。该函数到处都有返回语句,并且在每次返回之前都有一个恢复语句。这对我来说似乎很乱,更不用说抛出异常时的可怕了。

作为改进,我使用内部类定义提出了这种概括。这是一个示例驱动程序(类恢复器)。

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; }

使用 g++ 4.2.3 (-Wall) 的输出是:

一个在:0
乙在:0
中:1
内径:1
最深
d 结束:1
c结束:1
b 内端:1
b结束:0
结束:0

这是我对“b end”的期望。

我觉得在 Unwind 类中定义类恢复器有助于阻止滥用。

我的问题是,是否有一种通用且更安全的方法来做到这一点?我担心一生的问题。

编辑:请假设没有线程,但堆栈上的“下游”方法会根据此 b_active_ 标志更改行为。

4

5 回答 5

6

我同意 Adam Pierce 的观点,并且认为您应该更喜欢引用而不是指针:

template<typename T>
class restorer {
   T& ref_;
   T save_;
public:
   restorer(T& perm) : ref_(perm), save_(ref_) {};
   ~restorer() { ref_ = save_; }
};
于 2008-10-16T10:59:23.593 回答
3

我喜欢恢复器模板,但我可能会将模板放在 Unwind 类之外,甚至放在单独的头文件中,以便将来其他类可以重用它。这也会使它更具可读性。

于 2008-10-16T10:19:44.063 回答
0

我也会这样做。这样,如果函数由于某种原因抛出或提前返回,您的 Restorer 对象将被销毁并且变量重置为原始值。真正的问题是,为什么需要在函数返回时恢复一个变量?对象是否从多个线程中使用?

量子皮特

于 2008-10-16T10:14:32.610 回答
0

我根据评论对示例进行了更多修改,并将其作为社区 Wiki 答案而不是编辑问题。

/// 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
于 2008-11-01T04:21:19.933 回答
0

请注意这个旧线程的未来读者,而不是这样使用它:

 restorer<bool> trust_in_the_stack(&b_active_);

当我使用 C++11 时,我定义了:

 #define restorer_macro(var) restorer<decltype(var)> restorer_##named{var};

所以现在使用如下:

 restorer_macro(b_active_);

这避免了知道类型和命名对象,因此它一直持续到范围结束。当然,您可能更喜欢重命名宏“restorer”,然后重命名类,例如“restorer_container”。

于 2020-08-24T14:36:02.627 回答