Lets say, for example, that I have a class that requires the use of some old C stuff (like pthreads or something), so for one reason or another, I’ve ended up with a malloc() call in my constructor, like so:
class Foo
{
public:
Foo()
{
someMutex = malloc(sizeof(pthread_mutex_t));
pthread_mutex_init(someMutex);
someString = new string("yay string!");
}
private:
pthread_mutex_t * someMutex;
string * someString;
}
It seems like there is a lot of misinformation about destructors out there. I keep seeing examples of explicitly defined destructors calling delete on pointer based members, but I also keep reading that I don’t need to explicitly define a destructor for a class to manage memory; all I need a destructor for are things like file handle cleanups and the like.
Thus leads to my question then: Even though someMutex was allocated with malloc and not the C++ new command, will the implicitly defined destructor still take care of it, or do I have to do it?
Also, lets just settle another question of mine, since its so closely related. In the class above, do I need to explicitly define a destructor in order to call delete on someString, or is that taken care of for me?
Not only do you need to define a destructor to do the cleanup, you also need to declare (and optionally define) a copy constructor and copy assignment operator for your class, to ensure that copies are made correctly.
The implicitly-defined destructor destroys member variables. So, for example, if you had a member variable of type
string, the destructor will destroy that variable all on its own. However, the destructor for a pointer (likestring*) is a no-op: you are responsible for destroying the pointed-to object.You also need to define the copy operations for this class, or at least suppress generation of the default copy operations that the compiler provides for you. Why? Because by default, the copy operations just copy each member variable. So, if for example you were to write:
Both
xandyare destroyed at the end of the block. At this point, bothxandypoint to the same dynamically allocated mutex and string, so the mutex and string would be destroyed twice (once forxand once fory).The better option is not to use manual memory allocation at all. Rather, you should make
someStringa direct member of the class (i.e., declare itstring someString;) or you should use a smart pointer of some kind to manage its lifetime (likeunique_ptrorshared_ptr). Similarly, you should use a smart pointer with a custom deleter to manage the lifetime of the mutex, unless your class is noncopyable, in which case you can make the mutex a direct member of the class.