5

I spent over 2 hours finding this memory leak:

class Parent{
  ...
}

class Child:public Parent{
  std::string str;
  ...
}

int main(){
  Parent *ptr=new Child();
  delete ptr;
}

I fixed it by moving the string into the parent class. Why did this memory leak happen? Shouldn't the child's members be deleted as well?

Leo Jiang
  • 24,497
  • 49
  • 154
  • 284
  • 6
    Does Parent have a virtual destructor? You are deleting a Parent* so unless its destructor is virtual it will not call Child::~Child() – Tas Jul 13 '15 at 01:32
  • 4
    Note if `Parent` does not have a virtual destructor, the `delete` is Undefined Behavior, which is worse than a memory leak. – aschepler Jul 13 '15 at 01:40

1 Answers1

11

This can happen because Parent may not have a virtual destructor. Since you are creating a Parent* (the base class) to a dynamically allocated derived class Child, deleting the Parent* which has no virtual destructor will cause undefined behaviour, but typically will result in the derived class not being destroyed.

From Scott Myers - Effective C++ Third Edition:

... if we delete Base class pointer with a non-virtual destructor, results are undefined. What typically happens at runtime is that the derived part of the object is never destroyed. This is an excellent way to leak resources, corrupt data structures, and spend a lot of time with a debugger. So any class with virtual functions should almost certainly have a virtual destructor.

class Parent{
};

class Child:public Parent{
public:
    Child() : str("Child") {}
    ~Child() { std::cout << str << std::endl;};
    std::string str;
};

int main(){
    Parent *ptr=new Child();
    delete ptr; // undefined behaviour: deleting a Base* to a Derived object where the Base has no virtual destructor
}

You can fix this by making Parent's destructor virtual:

class Parent{
public:
    virtual ~Parent() {} // Will call derived classes destructors now as well
};

class Child:public Parent{
public:
    Child() : str("Child") {}
    ~Child() { std::cout << str << std::endl;};
    std::string str;
};

int main(){
    Parent *ptr=new Child();
    delete ptr;
    // Child::~Child() has now been called.
}

See When to use virtual destructors? which probably explains it better than I did

Edit: Thank you to the @aschepler (in the question's comments), commenters below, and the answer to the linked question, I have updated the answer to better reflect that this is undefined behaviour. In my haste I didn't mention it, and only mentioned the typical behaviour

Community
  • 1
  • 1
Tas
  • 7,023
  • 3
  • 36
  • 51
  • 3
    Actually, according to the C++ standard, it is undefined behavior to delete a derived object using a base class pointer, and the base class's destructor is not virtual. – PaulMcKenzie Jul 13 '15 at 02:00
  • @PaulMcKenzie often "undefined behavior" can be consistent and predictable, even if you can't rely on it. The lack of a virtual destructor very likely causes the symptoms noted in the question. – Mark Ransom Jul 13 '15 at 03:19
  • @Mark: the second sentence of the answer suggests that there is defined behavior ("only Parent deleted") which is in fact incorrect, as Paul notes. – MSalters Jul 13 '15 at 07:26
  • @MarkRansom Even if in cases where "undefined behavior" is predictable, you should never, ever consider it standard or even typical. – PC Luddite Jul 13 '15 at 08:28
  • Thank you to the above. I have updated the answer to include this. – Tas Jul 13 '15 at 09:45