2

クラスインスタンス変数を一時的に変更し、関数が完了したときにそれを復元する必要があるクラスインスタンス関数に出くわしました。関数にはあらゆる場所に return ステートメントがあり、それぞれの return の前に復元ステートメントがありました。例外がスローされたときの恐怖は言うまでもなく、それは私には面倒に思えました。

改善として、内部クラス定義を使用してこの一般化を思いつきました。サンプルのドライバ プログラム (クラス復元プログラム) を次に示します。

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
最も深い
エンドエンド: 1
cエンド:1
b内端:1
bエンド:0
終了: 0

これが「bエンド」に期待するものです。

クラス 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 に答える