Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why is std::string causing a memory leak in a class even after deleting

I am building a class that uses void* as a storage method. I am aware that it is not a good idea to use void*. But I don't know what value it will hold at compile time, so I thought it was the best solution. It works perfectly with everything, including C strings, but doesn't work with std::string. It causes a really bad memory leak. I've built a basic model of the class that has the same problem.

#include<iostream>
#include<string>

class CLASS {
public:
    void* data;

    CLASS(std::string str) {
        std::string* s = new std::string;
        *s = str;
        data = s;
    }

    ~CLASS() {
        delete data;
    }
};

int main() {
    std::string str = "hi";
        while (true)
        {
            CLASS val(str);
        }
}
like image 696
Grady Avatar asked Oct 12 '25 00:10

Grady


2 Answers

If you allocate a string, cast the pointer to a void *, then delete that, you will be trying to delete a void thing, not a string thing. For example, any class that allocates secondary storage (above and beyond the actual object being created with new) will most likely run into trouble:

#include <iostream>

class Y {
public:
    Y() { std::cout << "y constructor\n"; }
    ~Y() { std::cout << "y destructor\n"; }
};

class X {
public:
    X() { std::cout << "x constructor\n";  y = new Y(); }
    ~X() { delete y; std::cout << "x destructor\n"; }
private:
    Y *y;
};

int main() {
    X *x = new X();
    delete (void*)x;
}

This code will "work" in that it's legal but it will not do what you expect:

x constructor
y constructor

And a decent compiler should warn you about it:

program.cpp: In function ‘int main()’:
program.cpp:19:19: warning: deleting ‘void*’ is undefined [-Wdelete-incomplete]
   19 |     delete (void*)x;
      |                   ^

You should be deleting the type you allocated, to ensure the correct destructor is used. In other words, getting rid of the cast in the code shown above (so that you delete the correct type) will work out better:

x constructor
y constructor
y destructor
x destructor

Modern C++ has type-safe types which will do the heavy lifting for you, such as variant or any (if your single class needs to store a variety of types decided at run-time), or templates (if it can be used for one type but any of a variety). You should investigate those as an alternative to void *.

like image 124
paxdiablo Avatar answered Oct 14 '25 13:10

paxdiablo


delete data does not work. delete will call the destructor and then deallocate the storage for one object, but void cannot be destroyed nor does it have a size.

The destructor of std::string will release the memory it allocated, but it is not being called here. You at the very least need to store the destructor of the value held in data. std::any will handle this all for you.

However, consider restricting it to a few known types with a std::variant<T1, T2, T3, ...> instead.

like image 21
Artyer Avatar answered Oct 14 '25 14:10

Artyer