Search code examples
c++memory-leaksvalgrinddynamic-memory-allocationplacement-new

Why is there a memory leak in this program and how can I solve it, given the constraints (using malloc and free for objects containing std::string)?


This is a minimal working example for the problem I am facing in my real code.

#include <iostream>

namespace Test1 {
    static const std::string MSG1="Something really big message";
}

struct Person{
    std::string name;
};

int main() {
    auto p = (Person*)malloc(sizeof(Person));
    p = new(p)Person();
    p->name=Test1::MSG1;

    std::cout << "name: "<< p->name << std::endl;

    free(p);

    std::cout << "done" << std::endl;

    return 0;
}

When I compile it and run it via Valgrind, it gives me this error:

definitely lost: 31 bytes in 1 blocks


Constraints

  1. I am bound to use malloc in the example above, as in my real code I use a C library in my C++ project, which uses this malloc internally. So I can't get away from malloc usage, as I don't do it explicitly anywhere in my code.
  2. I need to reassign std::string name of Person again and again in my code.

Solution

  • The important pieces of your code line by line...

    Allocate memory for one Person object:

    auto p = (Person*)malloc(sizeof(Person));
    

    Construct a Person object in that already allocated memory via calling its constructor:

    p = new(p)Person();
    

    Free the memory allocated via malloc:

    free(p);
    

    Calling the constructor via placement new creates a std::string. That string would be destroyed in the destructor but the destructor is never called. free does not call destructors (just like malloc does not call a constructor).

    malloc only allocates the memory. Placement new only constructs the object in already allocated memory. Hence you need to call the destructor before calling free. This is the only case I am aware of where it is correct and necessary to explicitly call a destructor:

    auto p = (Person*)malloc(sizeof(Person));
    p = new(p)Person();
    p->~Person();
    free(p);