olp-1w
Last Updated: February 25, 2016
·
761
· flipcoder

Exception-safe constructors (using scoped_dtor)

If you're like me, you love RAII. RAII is the elegance of initializing objects inside constructors, and dealing with all destruction code in the destructor.

One thing that can mess you up, is when something decides to throw an exception somewhere inside your constructor.

If this happens, your destructor is not called. Sometimes this is not what you want. There are ways to avoid this, however, this is what I use:

template <class T>
class scoped_dtor {
    private:
        T* m_object;
    public:
        scoped_dtor(T* o):
            m_object(o)
        {}

        ~scoped_dtor(){
            call();
        }

        void call() {
            if(m_object)
                m_object->~T();
        }

        void resolve() {
            m_object = NULL;
        }
};

To use the scoped_dtor inside a constructor do this:

MyClass :: MyClass()
{
    // make sure destructor can be run safely here
    // this means zeroing pointers, etc.

    scoped_dtor<MyClass> dtor(this);

    // the bulk of your destruction code goes here...

    dtor.resolve();  // don't forget this
}

If anything throws between the scoped_dtor being declared and the dtor.resolve() call, your destructor will be called safely.

EDIT:

When not to use:

  • It is not necessary if your destructor is blank.

  • Use smart pointers instead if your destructor contains only calls to delete.

  • If the "responsibilities" that your destructor takes care of are used in more than one place in your code, it is better to wrap those in their own classes separately.

However, in the cases when an object needs to explicitly handle more than one unqiue responsibility, this can prevent an exception half way through your constructor from being difficult to handle. In other words, carefully consider the alternatives before using this.

2 Responses
Add your response

3470

Hi Grady, I jump in to make some comments on your posts. Apart from the fact that I would not try to prevent issues with exception thrown from a constructor (but likely you've got special needs that gives sense to your method then), I would use standard smart pointers to perform the job: auto_ptr or better scope_ptr in pre-c++11, or even better unique_ptr in c++11.

over 1 year ago ·
3477

@yifu I agree that smart pointers are definitely the way to go most of the time, especially if all you have are calls to delete. But every so often, you'll have multiple responsibilities for your class that require additional code, and in the case that a class handles more than one thing (and if those individual items would be too tedious to wrap into their own classes), this method works well. I would assume the actual runtime overhead is less using this method, since the template generates 1 class per usage instead of one class for every responsibility.

over 1 year ago ·