我读过有关范围卫士的文章(通用:改变你编写异常安全的代码 - 永远的)在DDJ和我理解他们的共同使用

然而,常见的用途是实例化堆栈上的特定堆叠防护件对特定操作的,e.g:

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

显然,上面的例子中是行不通的,因为topSecret将沿着与所述的的范围关闭。我想要范围保护图案,我可以很容易地排队我确定有需要的在运行时清理操作。有没有可用的这样呢?

我无法推送范围后卫对象到一个标准的队列,导致原始对象(一个我推)将在此过程中被解雇。如何推动堆上分配的堆栈后卫和使用它删除在析构函数成员一个队列?有没有人有一个更聪明的方法呢?

有帮助吗?

解决方案

看来你不明白它是什么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活字这么多,你表现出了兴趣:这是你如何能结合使用的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() );
}

(未测试的)

请务必检查戴维的高效移动值类型博客

其他提示

咦,原来的DDJ范围后卫是“可移动的”,而不是在C ++ 0X感,但在相同的意义上,一个auto_ptr是可移动的:复制构造函数过程中,新的后卫“解散”老后卫(auto_ptr这样的拷贝构造函数调用旧的的的auto_ptr ::发布的)。

所以,我可以简单地保持queue<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));
   // ...
}

顺便说一句,感谢您对上述问题的答案。这是信息和教育给我以不同的方式。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top