我遇到了一个需要暂时更改类实例变量的类实例函数,然后在函数完成时将其恢复。该函数在整个地方都有返回语句,并且在每次返回之前都有一个恢复语句。这对我来说似乎很混乱,更不用说在抛出异常时会感到害怕。
作为一项改进,我使用内部类定义提出了这种泛化。这是一个示例驱动程序(类恢复程序)。
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)的输出是:
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
我期待“b end”。
我觉得在Unwind类中定义类恢复器有助于阻止滥用。
我的问题是,有没有一般和更安全的方法来做到这一点? 我担心终身问题。
编辑:请假设没有线程,但堆栈上的“下游”方法会根据此b_active_标志更改行为。
答案 0 :(得分:6)
我同意Adam Pierce并且认为你应该更喜欢引用而不是指针:
template<typename T>
class restorer {
T& ref_;
T save_;
public:
restorer(T& perm) : ref_(perm), save_(ref_) {};
~restorer() { ref_ = save_; }
};
答案 1 :(得分:3)
我喜欢恢复器模板,但我可能会将模板放在Unwind类之外,甚至放在单独的头文件中,以便将来可以被其他类重用。这也会使它更具可读性。
答案 2 :(得分:0)
我也是这样做的。这样,如果函数抛出或由于某种原因提前返回,则Restorer对象将被销毁,变量将重置为原始值。问题是,为什么你需要有一个在函数返回时被还原的变量?对象是否来自多个线程?
QuantumPete
答案 3 :(得分: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
答案 4 :(得分: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”。