Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Throwing exception from a function parameter

Does the code below produce a memory leak?

#include <utility>
#include <stdexcept>

struct A {};

struct B {
    B() {
        throw std::runtime_error("");
    }
};

template <class T>
class MyPtr {
public:
    MyPtr(T* p) : m_p(p) {}

    MyPtr(MyPtr&& other) : m_p(other.m_p) {
        other.m_p = nullptr;
    }

    ~MyPtr() {
        delete m_p;
    }

private:
    T* m_p;
};

class Foo {
public:
    Foo(MyPtr<A> a, MyPtr<B> b) : m_a(std::move(a)), m_b(std::move(b)) {}

private:
    MyPtr<A> m_a;
    MyPtr<B> m_b;
};

int main() {
    try {
        Foo foo(new A(), new B());
    }
    catch (const std::exception&) {
    }

    return 0;
}

Is there a difference between

Foo foo(new A(), new B());

and

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Foo foo(MyPtr(new A()), MyPtr(new B()));

?

>Solution :

The evaluation order is based on sequenced-before and sequenced-after in modern C++ versions. This means, the following will happen:

  • in any non-overlapping order: new A(), new B() – memory is allocated for A and B and constructors are run (these cannot be interleaved)
  • since B has a ctor, this ctor is run (sequenced-after allocation)
  • B‘s ctor throws
  • since B‘s ctor didn’t complete, the memory allocated by new for constructing B will be freed, without calling dtor.

Note that the call to Foo‘s ctor would be sequenced-after the above, so it won’t be reached.

Thus, B is not leaked. However, function parameters can be sequenced in any non-overlapping way, so A might or might not be leaked. This might, in theory, change from run to run.

If you do: Foo foo(MyPtr(new A()), MyPtr(new B()));, then you immediately encapsulate the pointers to smart pointers after new, so it won’t leak.

Also, it’s worth mentioning, while the memory for B is deallocated, any memory that’s allocated in B‘s ctor and not deallocated till throw can still potentially leak. In this example there’s no such allocation, but in actual codes it might occur. The only memory deallocated is that of the entire object being constructed.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading