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上删除其成员的队列?有没有人有更聪明的方法?
看来你并不欣赏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关于高效可移动值类型的博客