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);
}
}
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 *
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With