关于c ++:在类构造函数的异常之后清理数据

Cleaning data after exception on class constructor

为什么此代码不调用类析构函数中的CloseHandles?在代码测试中,我显式调用"((myClass*)pthis)->CloseHandles();",但变量m_bFinished的值错误。为什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#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;
}

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

另外,您实际上不是抛出std::exception,而是指向它的指针:

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

编辑:我注意到你也捕捉到了一个指针,所以这不是问题所在。但是,没有采用字符串文字的std::exception的构造函数,所以我想知道您的代码是如何编译的。

在任何情况下,如果一个构造函数可能在分配了一个原始资源之后抛出,那么您就有一个潜在的泄漏。

您需要将资源包装在管理它的类中——可能是一个智能指针或类似的raii包装器。并使用成员初始值设定项列表!

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

用一些代码说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 */ }
};

以及一个构造函数委托示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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!