虚析构函数中的异常处理

Exception handling in virtual destructor

本文关键字:异常处理 析构函数      更新时间:2023-10-16

我在Stack上分配了两个数据库类对象。我在我自己定义的类中继承了这个数据库类。现在数据库类有一个名为"Close()"的函数,如果关闭该数据库实例失败,则抛出异常。现在,对于客户端透视图,我不希望客户端每次都调用close()来关闭连接,而是我想在数据库类中声明一个虚拟的structor,当对象超出范围时,它将自动关闭连接。现在我面临的问题是,如果我创建两个DB对象,现在尝试删除两者,如果关闭成功,这很好,但如果关闭失败的第一个对象,它抛出一个异常,现在第二个仍然被分配(如果动态分配成员对象),这导致内存泄漏。如何处理这种情况

你不会从析构函数抛出异常。参见:http://www.stroustrup.com/bs_faq2.html#ctor-exceptions
Bjarne quote(你能从d.r t抛出异常吗):

Not really:可以在析构函数中抛出异常,但该异常不能离开析构函数;如果析构函数通过抛出退出,则可能发生各种糟糕的事情,因为将违反标准库和语言本身的基本规则。不要这样做。

你可能不想在堆栈上分配任何DB对象,一个安全的,可重用的方法是使用连接池:https://en.wikipedia.org/wiki/Object_pool_pattern

像这样的

,每个约定对象的销毁是在程序存在时进行的,在那里您可以将关闭作为池的整体部分来处理,而不是在一些随机函数

中展开。

您可能应该有一个类用于保存一个数据库连接,另一个类用于保存第一个数据库连接的两个实例。由于我认为数据库的创建也可能失败,因此将每个连接放在自己的类中(带有自己的析构函数)将非常好地处理部分构造/析构。

此外,如果析构函数抛出,您可以做的事情很少(除了记录条件)。

不要从析构函数抛出异常,否则会发生不好的事情,使用shutdown()方法并在析构函数外部调用它。大多数c++的东西都是在析构函数中清理的,如果你从析构函数中抛出,你实际上就停止了清理,你最终会得到一个未定义状态的c++程序:

class Database{
    std::vector<int> v;
    connection       c;
public:
    ~Database(){
        c.close(); //ouch throws  v don't get deallocated. (neither c)
    }
};

可以改成:

class Database{
    std::vector<int> v;
    connection       c;
public:
    ~Database(){
         // v deallocated here
    }
    void shutdown(){
        c.close();
    }
};

main.cpp

int main(){
    Database d;
    d.shutdown(); //even if d throws, d destructor will be called.
                  //and as consequence of d destructor also c & v 
                  //destructors are called.
    return 0;
}

因为你有两个数据库

int main(){
    Database d1,d2;
    try{
        d1.shutdown();
    }catch( /** correct exception type*/){
        //error handling for 1
    }
    try{
        d2.shutdown();
    }catch( /** correct exception type*/){
        //error handling for 2
    }
    //both destructors called
    return 0;
}

如果close失败,无论如何都没有好的恢复路径,您最好记录错误,然后继续,就好像它成功了一样。因此,我建议您在析构函数的try块中调用close,并在相应的catch块中记录错误。析构函数可以正常完成。

正如上面@David提到的,析构函数永远不应该泄漏异常,这是因为当异常发生时调用析构函数,而异常中的异常导致崩溃。

永远不应该在析构函数调用中抛出异常。不应该调用任何可能导致它们出现的东西,否则,你会破坏你的类安全。

你应该添加一个函数来检查数据库对象是否可以"关闭",而不是一个异常。如果你想象你只是不能调用函数中的析构函数,可能会抛出异常,你会找到解决方案。

我的选择是不使用析构函数来销毁关联的对象,如果有可能不允许这样做的话。

析构函数仅用于释放内部对象资源。您可以创建类似resource manager的东西,它将存储指向您需要控制的所有对象的指针。这些可能是shared pointers。让这个类来定义你的行为,而不是对象本身。