In C++, it's generally considered a good strategy to throw exceptions in constructors when something goes wrong.
But, I'm confused about cleaning up the actual memory allocated for the failed object, when the failed object is not a stack variable.
Consider:
class Foo
{
public:
Foo()
{
throw std::runtime_error("Fail!");
}
};
int main()
{
Foo* f;
f = new Foo();
}
If I run this program using valgrind --leak-check=full
, it reports:
=24981== 34 bytes in 1 blocks are possibly lost in loss record 2 of 3
==24981== at 0x4C286E7: operator new(unsigned long) (vg_replace_malloc.c:287)
==24981== by 0x4EEE998: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17)
==24981== by 0x4EF0384: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17)
==24981== by 0x4EF0462: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17)
==24981== by 0x400D14: Foo::Foo()
...which indicates to me that the memory allocated for the Foo
instance was never reclaimed. Of course, this makes sense, because in C++, the new
operator is actually doing two things. First, it's allocating a raw block of memory (similar to malloc
), and then it's initializing the raw memory by calling the constructor Foo::Foo()
. But since Foo::Foo()
throws an exception, the initial raw memory allocated is never reclaimed.
So, how can I reclaim it? I can't catch the exception and delete
the object, because I don't even have a pointer to the object at the time the exception is thrown.
The only thing I can think of is to separate allocation and initialization by using placement new. But most C++ programmers don't even know about placement new, so I doubt this obscure feature is really the way to go for a seemingly common problem.
So, what is the correct strategy here (assuming stack allocation is not an option)?