What to do when an exception is thrown on the initialization list when allocating memory for a raw pointer? The situation is easy if your class only has one raw pointer member, but it gets complicated with two or more. Here’s a code example that’s guaranteed to leak memory if the second...
new int
...throws an exception (because the destructor will not be called):
struct bad
{
bad() : p1(new int), p2(new int)
{
*p1 = 1;
*p2 = 2;
}
~bad()
{
delete p1;
delete p2;
}
int* p1;
int* p2;
};
There is no way to free the memory allocated to...
p1
...if...
p2(new int)
...throws! Let’s build on my previous example and see what happens if we use a function try
-catch
block on the constructor:
struct still_bad
{
still_bad() try : p1(new int), p2(new int)
{
*p1 = 1;
*p2 = 2;
}
catch(...)
{
if(p1) delete p1; if(p2) delete p2;
}
~still_bad()
{
delete p1;
delete p2;
}
int* p1 = nullptr;
int* p2 = nullptr;
};
Still no good! Because accessing...
p1
...and...
p2
...in the catch
block leads to undefined behavior. See here.
The only way to guarantee correct behavior is to use smart pointers. This works because:
- the initialization list allocates in pre-defined order (the order of member declaration) and
- the destructors of already created members will be called.
Here’s the correct way of allocating multiple pointers:
struct good
{
good() : p1(make_unique<int>()), p2(make_unique<int>())
{
*p1 = 1;
*p2 = 2;
}
unique_ptr<int> p1;
unique_ptr<int> p2;
};
This is guaranteed to do proper cleanup if the second...
make_unique<int>()
...throws...
std::bad_alloc
#include <iostream>
#include <memory>
using namespace std;
struct bad
{
bad() : p1(new int), p2(new int)
{
*p1 = 1;
*p2 = 2;
}
~bad()
{
delete p1;
delete p2;
}
int* p1;
int* p2;
};
struct still_bad
{
still_bad() try : p1(new int), p2(new int)
{
*p1 = 1;
*p2 = 2;
}
catch(...)
{
if(p1) delete p1; if(p2) delete p2;
}
~still_bad()
{
delete p1;
delete p2;
}
int* p1 = nullptr;
int* p2 = nullptr;
};
struct good
{
good() : p1(make_unique<int>()), p2(make_unique<int>())
{
*p1 = 1;
*p2 = 2;
}
unique_ptr<int> p1;
unique_ptr<int> p2;
};
int main()
{
bad b;
still_bad sb;
good g;
}