动态创建范围保护

Ily*_*lya 4 c++ design-patterns scopeguard

我已经在DDJ中阅读了关于范围保护的文章(通用:改变你编写异常安全代码的方式 - 永远),我理解它们的常用用法.

但是,常见的用途是在堆栈上为特定操作实例化特定堆栈保护,例如:

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

但是,如果我想在运行时安排清理操作,例如当我有一个循环时,该怎么办:

{
   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
   }
}
Run Code Online (Sandbox Code Playgroud)

显然,上面的例子不起作用,因为topSecret它将与for范围一起关闭.我想要一个范围保护模式,我可以很容易地排队清理操作,我确定在运行时需要它.有这样的东西吗?

我无法将范围保护对象推入标准队列,导致原始对象(我正在推送的对象)在此过程中被解除.如何推送堆分配的堆栈保护并使用在dtor上删除其成员的队列?有没有人有更聪明的方法?

sel*_*tze 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

要么:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

或者在"C++ 0x"(即将推出的C++标准)中:

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

编辑:因为我非常喜欢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() );
}
Run Code Online (Sandbox Code Playgroud)

(另)

请务必查看Dave关于高效可移动值类型的博客

  • auto_ptr <...>的向量/队列显然不是一个好主意,因为auto_ptrs不可复制! (4认同)