The following code causes a memory leak:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class base
{
void virtual initialize_vector() = 0;
};
class derived : public base
{
private:
vector<int> vec;
public:
derived()
{
initialize_vector();
}
void initialize_vector()
{
for (int i = 0; i < 1000000; i++)
{
vec.push_back(i);
}
}
};
int main()
{
for (int i = 0; i < 100000; i++)
{
unique_ptr<base> pt = make_unique<derived>();
}
}
It didn't make much sense to me, since class derived allocates no raw dynamic memory, and unique_ptr deallocates itself. I get that class base's implicit destructor is being called instead of derived's, but I don't get why that's a problem here. If I were to write an explicit destructor for derived, I wouldn't write anything for vec.
Best Answer
When the compiler goes to execute the implicit
delete _ptr;
inside of theunique_ptr
's destructor (where_ptr
is the pointer stored in theunique_ptr
), it knows precisely two things:_ptr
is. Since the pointer is inunique_ptr<base>
, that means_ptr
is of the typebase*
.This is all the compiler knows. So, given that it is deleting an object of type
base
, it will invoke~base()
.So... where's the part where it destroys the
dervied
object that it actually points to? Because if the compiler doesn't know that it is destroying aderived
, then it doesn't knowderived::vec
exists at all, let alone that it should be destroyed. So you've broken the object by leaving half of it undestroyed.The compiler cannot assume that any
base*
being destroyed is actually aderived*
; after all, there could be any number of classes derived frombase
. How would it know which type this particularbase*
actually points to?What the compiler has to do is figure out the correct destructor to call (yes,
derived
has a destructor. Unless you= delete
a destructor, every class has a destructor, whether you write one or not). To do this, it will have to use some information stored inbase
to obtain the right address of the destructor code to invoke, information that is set by the constructor of the actual class. Then it has to use this information to convert thebase*
to a pointer to the address of the correspondingderived
class (which may or may not be at a different address. Yes, really). And then it can invoke that destructor.That mechanism I just described? It is commonly called "virtual dispatch": aka, that thing that happens any time you call a function marked
virtual
when you have a pointer/reference to a base class.If you want to call a derived class function when all you have is a base class pointer/reference, that function must be declared
virtual
. Destructors are fundamentally no different in this regard.