让我们有一段代码(fstream只是一个例子,我们可以谈论动态内存分配......):
fstream f;
try {
f.open("xxx");
...
f.close();
} catch (...) {
...
}
当出现问题时,我想关闭()文件(释放内存或其他),但我不知道f的状态。毕竟,异常可能来自f.open()。我认为在catch子句中调用f.close()是不安全的,因为我不能再相信f。
f也可以是一个指向动态分配的数组的指针,我想删除[],但谁知道抛出异常后指向的位置...
这可能不是很常见,但如果我绝对无法承受任何额外伤害,我该怎么办?
我可以考虑立即中止()。
感谢。
答案 0 :(得分:11)
您应该使用RAII或在此广为人知的 SBRM (基于范围的资源管理):)
答案 1 :(得分:10)
fstream
析构函数为您调用close
。抛出异常时,文件会自动关闭。
为了管理内存,您可以使用智能指针。
为了管理互斥锁或更常用的锁,大多数库为您提供了一个类,其析构函数为您解锁互斥锁。
永远不要以下列形式编写代码:
acquire a resource
do stuff which can throw
release a resource
相反,请使用析构函数为您释放资源的对象。
答案 2 :(得分:4)
fstream析构函数会为你调用close(),所以你不需要自己关闭它(除非你想看到close()的返回码)。
答案 3 :(得分:2)
在您的示例中,您可以将f
的声明移到try
块中,以确保它会自行销毁;析构函数知道对象的状态。
作为另一个例子,通过内存分配,您可以在实际分配内存之前将指针初始化为0,然后在释放分配的内存时再次将其重置为零。这让你检查是否分配了内存以避免释放不再属于你的内存。例如:
char *name = 0;
try {
// perform some operations which may throw...
// now allocate
name = new char[20];
// more error prone operations here
}
catch(...){
if(name != 0){
delete[] name;
name = 0;
}
}
同样,你也可以在这里使用RAII。例如:
class MyCharArray {
char *str;
public:
MyCharArray(size_t size){
str = new char[size];
}
~MyCharArray(){
delete[] str;
}
};
int main(){
try {
// perform some operations which may throw...
MyCharArray name(20);
// more error prone operations here
}
catch(...){
// no additional error handling required here
}
return 0;
}
请注意,RAII被认为是优越的,因为您只在析构函数中编写一次清理代码 - 而不是在每个try
块之后编写清理代码。
答案 4 :(得分:1)
在fstream对象上启用异常,并在可能的位置处理异常:
void foo()
{
std::fstream f( "lala.txt" );
f.exceptions( std::fstream::failbit | std::fstream::badbit )
// do something
}
答案 5 :(得分:0)
我不会考虑使用提升来使RAII作弊:
#include <boost/smart_ptr.hpp>
#include <fstream>
using namespace std;
static void do_close(fstream* f)
{
try
{
f->close();
delete f;
} catch(...) { /* log the error? */ }
}
int main()
{
boost::shared_ptr<fstream> f(new fstream(), &do_close);
try
{
f->open("xxx");
f->close();
} catch (...)
{
}
}