Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a string in a C++ subclass cause memory leaks?

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?

like image 750
Leo Jiang Avatar asked Jul 13 '15 01:07

Leo Jiang


People also ask

What is the main cause of memory leaks in C?

Memory leaks occur when new memory is allocated dynamically and never deallocated. In C programs, new memory is allocated by the malloc or calloc functions, and deallocated by the free function.

What is memory leak problem in C?

The memory leak occurs, when a piece of memory which was previously allocated by the programmer. Then it is not deallocated properly by programmer. That memory is no longer in use by the program. So that place is reserved for no reason. That's why this is called the memory leak.

How can you prevent memory leak in C which?

C. To avoid memory leaks, memory allocated on heap should always be freed when no longer needed.

How are memory leaks caused in C++?

Memory leakage occurs in C++ when programmers allocates memory by using new keyword and forgets to deallocate the memory by using delete() function or delete[] operator. One of the most memory leakage occurs in C++ by using wrong delete operator.


1 Answers

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

like image 60
Tas Avatar answered Nov 04 '22 11:11

Tas