4

DDJのスコープガード(一般:例外安全コードの記述方法を変更する—永遠に)に関する記事を読みましたが、その一般的な使用法を理解しています。

ただし、一般的な使用法は、特定の操作のためにスタック上の特定のスタックガードをインスタンス化することです。

{
    FILE* topSecret = fopen("cia.txt");
    ON_BLOCK_EXIT(std::fclose, topSecret);
    ... use topSecret ...
} // topSecret automagically closed

しかし、実行時にクリーンアップ操作をスケジュールしたい場合、たとえばループがある場合はどうなりますか?

{
   vector<FILE*> topSecretFiles;
   for (int i=0; i<numberOfFiles; ++i)
   {
      char filename[256];
      sprintf(filename, "cia%d.txt", i);
      FILE* topSecret = fopen(filename);
      topSecretFiles.push_back(topSecret);
      ON_BLOCK_EXIT(std::fclose, topSecret); // no good
   }
}

明らかに、forスコープtopSecretとともに閉じられるため、上記の例は機能しません。実行時に必要であると判断したクリーンアップ操作を同じように簡単にキューに入れることができるスコープガードパターンが必要です。このようなものはありますか?

スコープガードオブジェクトを標準キューにプッシュできません。元のオブジェクト(プッシュしているオブジェクト)がプロセスで却下されるためです。ヒープに割り当てられたスタックガードをプッシュし、dtorのメンバーを削除するキューを使用するのはどうですか?誰かがもっと賢いアプローチを持っていますか?

4

2 に答える 2

6

RAIIが何であるかを評価していないようです。これらのスコープガードは、ローカル(「スコープ」)の場合には便利ですが、RAIIが実際に行うことになっていること、つまりリソー​​スをオブジェクトにカプセル化することを優先して、それらを回避するようにしてください。タイプFILE*は、実際にはそれが得意ではありません。

別の方法は次のとおりです。

void foo() {
    typedef std::tr1::shared_ptr<FILE> file_sptr;
    vector<file_sptr> bar;
    for (...) {
        file_sptr fsp ( std::fopen(...), std::fclose );
        bar.push_back(fsp);
    }
}

または:

void foo() {
    typedef std::tr1::shared_ptr<std::fstream> stream_sptr;
    vector<stream_sptr> bar;
    for (...) {
        file_sptr fsp ( new std::fstream(...) );
        bar.push_back(fsp);
    }
}

または「C++0x」(今後のC ++標準)の場合:

void foo() {
    vector<std::fstream> bar;
    for (...) {
        // streams will become "movable"
        bar.push_back( std::fstream(...) );
    }
}

編集:私はC ++ 0xの活字がとても好きで、それに興味を示したので:ref-countingオーバーヘッドなしでunique_ptrをFILE*と組み合わせて使用​​する方法は次のとおりです。

struct file_closer {
    void operator()(FILE* f) const { if (f) std::fclose(f); }
};

typedef std::unique_ptr<FILE,file_closer> file_handle;

file_handle source() {
    file_handle fh ( std::fopen(...) );
    return fh;
}

int sink(file_handle fh) {
    return std::fgetc( fh.get() );
}

int main() {
    return sink( source() );
}

(未テスト)

効率的な可動値タイプに関するDaveのブログを必ずチェックしてください。

于 2009-10-14T11:32:50.230 に答える
0

DDJスコープガードはC++0xの意味ではなく、auto_ptrが移動可能であるのと同じ意味で「移動可能」であることがわかります。コピーコンストラクター中に、新しいガードは古いガードを「却下」します(auto_ptrのように) copy ctorは、古いもののauto_ptr :: releaseを呼び出します)。

だから私は単にaqueue<ScopeGuard>を維持することができ、それはうまくいくでしょう:

queue<ScopeGuard> scopeGuards;

// ...

for (...)
{
   // the temporary scopeguard is being neutralized when copied into the queue,
   // so it won't cause a double call of cleanupFunc
   scopeGuards.push_back(MakeScopeGuard(cleanupFunc, arg1));
   // ...
}

ちなみに、上記の回答ありがとうございます。それは私にとってさまざまな方法で有益で教育的でした。

于 2009-10-14T13:35:35.253 に答える