0

Why this code not call CloseHandles in class destructor?
In my code test I call '((MyClass*)pThis)->CloseHandles();' explicitly, but variable m_bFinished have wrong value. Why ?

#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;
}
James Wiseman
  • 29,946
  • 17
  • 95
  • 158
lsalamon
  • 7,998
  • 6
  • 50
  • 63

1 Answers1

3

Because the destructor of a class doesnt' run when its constructor throws - the object hasn't been fully initialized yet.

Also, you're not actually throwing std::exception, but a pointer to it:

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

EDIT: I noticed that you're catching a pointer, too, so that's not the problem. But, there's no constructor of std::exception that takes a string literal, so I wonder how your code even compiles.

In any case, if a constructor can possibly throw after a raw resource has been allocated, you have a potential leak.

You need to wrap the resource in class that manages it - a smart pointer perhaps or a similar RAII wrapper. And use member initializer lists!

Another option is constructor delegation (new in C++11). An object is considered fully constructed when any of its constructors finishes execution. This means that if an exception is thrown from a constructor that delegated to another constructor (where you'd do the acquisition of handles), the destructor will be called.

To illustrate with some code:

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 */ }
};

And an example of constructor delegation:

#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) {};
}

Output is dtor!.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • But throwing 'throw std::exception("Exception thrown!")' and catching with 'catch(std::exception e)' the CloseHandles() still does not run. – lsalamon Mar 25 '14 at 14:18
  • 1
    As I said, a class' destructor *does not* run if its constructor throws. – jrok Mar 25 '14 at 14:19
  • In my code I call '((MyClass*)pThis)->CloseHandles();' explicitly, but variable m_bFinished have wrong value. Why ? – lsalamon Mar 25 '14 at 14:22
  • Not sure, what is the value supposed to be? As I said, the code doesn't compile and I can't try it. – jrok Mar 25 '14 at 14:29