我已经在DDJ中阅读了有关范围保护(Generic: Change the Way You Write Exception-Safe Code — Forever)的文章,我理解它们的常用用途。
然而,常见的用途是在堆栈上为特定操作实例化特定的堆栈保护,例如:
{
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
将与 for 范围一起关闭。我想要一个范围保护模式,我可以轻松排队清理操作,我确定需要在运行时。有没有这样的东西?
我无法将范围保护对象推入标准队列,导致原始对象(我正在推送的对象)在此过程中被解除。如何推送堆分配的堆栈保护并使用在dtor上删除其成员的队列?有没有人有更聪明的方法?
答案 0 :(得分: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中的可移动类型并且你对它表现出兴趣:以下是如何将unique_ptr与FILE * 结合使用而不需要任何重新计算开销:< / p>
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() );
}
(未测试的)
答案 1 :(得分:0)
嗯,事实证明DDJ范围保护是“可移动的”,不是在C ++ 0x意义上,但在同一意义上,auto_ptr是可移动的:在复制ctor期间,新警卫“解散”旧守卫(比如auto_ptr的copy ctor调用旧的 auto_ptr :: release )。
所以我可以简单地保留一个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));
// ...
}
顺便说一下,谢谢你的回答。它以不同的方式给我提供了丰富的信息和教育。