首页 文章

在类构造函数上的异常之后清理数据

提问于
浏览
0

为什么这段代码不会在类析构函数中调用CloseHandles?
在我的代码测试中,我明确地调用'((MyClass*)pThis)->CloseHandles();',但变量m_bFinished有错误的值 . 为什么?

#include <windows.h>
#include <exception>

class MyClass
{
public:

    explicit MyClass( void **pThis)
    {
        *pThis = this;
        m_bFinished = false;

        //code open handle here

        //an error occurs
        throw new std::exception("Exception thrown!");
    }

    ~MyClass()
    {
        if ( ! m_bFinished ) CloseHandles();
    }

    void CloseHandles()
    {
        if ( m_bFinished ) return;

        //close handles here.

        m_bFinished = true;
    }

private:
    bool m_bFinished;
};

int main(int argc, char* argv[])
{
    MyClass * pMyClass;
    void * pThis = NULL;

    try
    {
        pMyClass = new MyClass(&pThis);
    }
    catch(std::exception * e)
    {
        //delete pThis;

        if ( pThis )
        {
            ((MyClass*)pThis)->CloseHandles();
        }
    }

    return 0;
}

1 回答

  • 3

    因为类的析构函数在其构造函数抛出时不会运行 - 该对象尚未完全初始化 .

    另外,你实际上并没有抛出 std::exception ,而是指向它的指针:

    // dynamically allocates std::exception and throws a pointer to it
    throw new std::exception("Exception thrown!");
    

    编辑:我注意到你're catching a pointer, too, so that'不是问题 . 但是,没有 std::exception 的构造函数采用字符串文字,所以我想知道你的代码是如何编译的 .

    在任何情况下,如果构造函数可能在分配原始资源后抛出,则可能存在泄漏 .

    您需要将资源包装在管理它的类中 - 可能是智能指针或类似的RAII包装器 . 并使用member initializer lists

    另一种选择是构造函数委托(C 11中的新增) . 当一个对象的任何构造函数完成执行时,它被认为是完全构造的 . 这意味着如果从委托给另一个构造函数的构造函数抛出异常(您将在其中获取句柄),则将调用析构函数 .

    用一些代码来说明:

    struct Handle {
        Handle() : handle(new int()) {}
        ~Handle() { delete handle; }
        int* handle;
    };
    
    class MyClass {
        Handle h;
        MyFlass() : h() // handle initialized here
        {
           /**** code that may throw ****/
           // this will properly close handles because
           // the destructors of already initialized
           // members (like h) will be called
        }
        ~MyClass() { /* not called if constructor throws */ }
    };
    

    以及构造函数委派的示例:

    #include <iostream>
    
    class MyClass {
    private:
        int* handle;
        MyClass(int)  // dummy parameter just for overloading
            : handle(new int()) { /* better not throw from here */ }
    public:
        MyClass() : MyClass(0) // handle initialized here
        {
           /**** code that may throw ****/
           throw 42;
        }
        ~MyClass() { delete handle; std::cout << "dtor!"; }
    };
    
    int main()
    {
        try { MyClass m; } catch (int) {};
    }
    

    输出为 dtor! .

相关问题