C++ C++;安全异常处理

C++ C++;安全异常处理,c++,security,exception,C++,Security,Exception,让我们看一段代码(fstream只是一个示例,我们可以讨论动态内存分配…) 当出现问题时,我想关闭()文件(释放内存或其他),但我不知道f的状态。毕竟,异常可能来自f.open()。我认为在catch子句中调用f.close()是不安全的,因为我不再相信f f也可以是指向动态分配数组的指针,我想删除[],但谁知道抛出异常后它指向哪里 这可能不是很常见,但当我绝对不能承受任何额外的伤害时,我该怎么办 我可以考虑立即中止() 谢谢。您应该使用SBRM(基于范围的资源管理):close(); 删除f;

让我们看一段代码(fstream只是一个示例,我们可以讨论动态内存分配…)

当出现问题时,我想关闭()文件(释放内存或其他),但我不知道f的状态。毕竟,异常可能来自f.open()。我认为在catch子句中调用f.close()是不安全的,因为我不再相信f

f也可以是指向动态分配数组的指针,我想删除[],但谁知道抛出异常后它指向哪里

这可能不是很常见,但当我绝对不能承受任何额外的伤害时,我该怎么办

我可以考虑立即中止()


谢谢。

您应该使用SBRM(基于范围的资源管理):

析构函数调用
关闭
。当抛出异常时,文件将自动关闭

为了管理内存,可以使用智能指针

为了管理互斥锁或更通用的锁,大多数库为您提供了一个类,其析构函数为您解锁互斥锁

切勿以以下形式编写代码:

acquire a resource
do stuff which can throw
release a resource

相反,请使用其析构函数为您释放资源的对象。

fstream析构函数将为您调用close(),因此您实际上不需要自己关闭它(除非您希望看到close()的返回代码)。

在您的示例中,您可以将
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;
    }
}
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。例如:

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;
    }
}
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
块之后编写。请在fstream对象上启用异常,并在可能的位置处理异常:

void foo()
{
  std::fstream f( "lala.txt" );
  f.exceptions( std::fstream::failbit | std::fstream::badbit )
  // do something
}

我不会考虑使用Boost来做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 (...)
    {
    }
}
#包括
#包括
使用名称空间std;
静态无效do_关闭(fstream*f)
{
尝试
{ 
f->close();
删除f;
}catch(…){/*记录错误?*/}
}
int main()
{
boost::shared_ptr f(new fstream(),&do_close);
尝试
{
f->打开(“xxx”);
f->close();
}捕获(…)
{
}
}

+1因为RAII(SBRM)可用于不仅仅是fstream(其他响应似乎专注于fstream方面)。例如,如果将内存分配包装在智能指针中,那么RAII也应该在这里工作。@Alexandre C.好的,我明白了,但从技术上讲,在抛出异常后,我仍然可以信任应用程序(堆栈)?@Petr:异常会自动解除堆栈,即销毁任何本地对象并调用这些对象析构函数。因此,如果正常执行或发生异常,所有内容都将被释放/关闭/释放/无论发生什么。:)如果有什么东西使堆栈不平衡,比如缓冲区溢出,那么您通常就完蛋了。别让那样的事情发生@皮特:当这样的事情成为问题时,通常做任何事情都太迟了(除了调试)。您无法从代码中检测到这些问题。@Petr:您所说的标准称为“未定义的行为”。如果导致未定义的行为,那么根据定义,该行为是未定义的,并且无法做出任何保证。请注意,异常与未定义的行为有着本质的区别,因为它被很好地指定和管理。如果最后一次写入失败,即缓冲区刷新失败,则会引发显式关闭。析构函数中的自动关闭将永远不会关闭。如果您想知道您的文件写入一直成功到结束,您应该始终使用显式关闭。@Cashcow-我想您的意思是“…永远不会抛出”?如果接近释放资源,析构函数将尽最大努力。如果你想知道结果,你必须自己调用close。@CashCow:fixed:“…永远不会抛出(strike:close)”删除之前你不需要检查指针-删除空指针是有效和安全的。@molbdnilo:corrected。我仍然不需要如果。。。我想更多的是一种习惯。重要的部分是将指针设置为0并防止进一步的delete[]调用。