C++ string not freed when class is destroyed

Asked by Simon Rodan

The memory for a private string variable in a class appears not to be released automatically when the class is destroyed.

Here is a simple example...

#include <string>
#include <mcheck.h>

using namespace std;

class test
{

 string x;

 public:

 void set(const string& y)
 {
  x=y; // set x the private class string to the value of y
 }

 test() {}

 ~test() {}

};

int main()
{
 mtrace();

 // produces a leak
  test X;
  X.set("Hi");

 {
 // doesn't produce a leak
  test Y;
  Y.set("Hi");
 }

 muntrace();

 return 0;
}

And the memory trace it produces:

Memory not freed:
-----------------
           Address Size Caller
0x000000000187f460 0x1c at 0x7f3df25a926d

When the class is destroyed, the memory used by the private string, x, I think should be freed automatically. However it is not, causing a memory leak.

This seems to me to be a bug. Could someone who knows more than I do (i.e, everyone reading this), please confirm or refute my suspicion?

Question information

Language:
English Edit question
Status:
Solved
For:
Ubuntu gcc-defaults Edit question
Assignee:
No assignee Edit question
Solved by:
Eliah Kagan
Solved:
Last query:
Last reply:
Revision history for this message
Best Eliah Kagan (degeneracypressure) said :
#1

First, a matter of terminology: In C++, classes are not created and destroyed, being merely code constructs. *Objects*, which are *instances* of classes, are created and destroyed. So, in your example, X and Y are objects of type test, which is a class. (By the way, it's recommended to name classes that are not implementations of or vendor-supplied extensions to the C++ Standard Library starting with *capital* letters, and to name variables, including objects, starting with *lower case* letters. There are a number of differing coding standards used for writing C++ programs, but virtually all of them abide by those minimal naming rules.)

Your example shows that the memory for the object X is not released automatically between the mtrace() and muntrace() calls. That's because the object X still exists after the muntrace() call--it is automatically freed at the closing brace of the main() function (which, in this case, really means at the return statement). The object Y, which is local to the inner block, is automatically freed at the closing brace of that block (since that's where it goes out of scope), indicating that there is no memory leak when objects of type test are destructed.

In summary, the object X is still in scope and has *not* been destructed when you call muntrace(); therefore, its memory has not been freed.

To confirm this interpretation of what is happening, I suggest you create two alternate programs, one for each of the following modifications:

(1) Add logging to the constructor and destructor of the class test, to print messages when test objects are created and destroyed. Perhaps add some logging into main() as well (for example, right before mtrace(), right after mtrace(), right before muntrace(), right after muntrace()).

(2) Change the name of main() (for example, to f()), and create a new main() function whose body consists of "mtrace(); f(); muntrace();". Then no memory leak should be reported.

Do you have a different compiler that frees X *before* the muntrace() call in your example? That would be rather strange. If so, what compiler does that? When you disable optimizations, does it still do it?

Revision history for this message
Simon Rodan (simon-rodan) said :
#2

Thanks for your answer - this makes perfect sense. I tried #2) and your prediction is correct.

Revision history for this message
Simon Rodan (simon-rodan) said :
#3

Thanks Eliah Kagan, that solved my question.

Revision history for this message
mycae (mycae) said :
#4

Simon:

You may want to look at valgrind, which does emulated memory testing - you do not need to modify your program to trace the memory leaks, and it will report the total program results after your program completes entirely.

the downsides are the valgrind only runs under unix kernels, and not windows kernels, and that it is quite slow (order of magnitude).

It is simple to use:
valgrind NAMEOFPROGRAM

thats it -- it will even tell you the origin of the leak too, and other memory errors.

Revision history for this message
Simon Rodan (simon-rodan) said :
#5

Mycae

Thank you very much for the suggestion (for some reason I had assumed that Valgrind was a commercial product).

Kind regards

Simon