Is this the correct way to deallocate the memory of dude1
and to destroy the object dude1
?
Yes, it is technically correct.
But, it is not the best option. A better option would be to create the Dude
object in automatic storage, and not worry about its deallocation/cleanup at all, let the compiler worry about that for you, eg:
int main(int argc, char* argv[])
{
Dude dude1(21);
std::cout << dude1.age << '\n';
return 0;
}
If you must create the object dynamically, at least use a smart pointer like std::unique_ptr
so you don't need to delete
the object manually, eg:
#include <memory>
int main(int argc, char* argv[])
{
auto dude1 = std::make_unique<Dude>(21);
// prior to C++14, use this instead:
// std::unique_ptr<Dude> dude1(new Dude(21));
std::cout << dude1->age << '\n';
return 0;
}
What memory deallocation and cleanup is the destructor doing if left as the default destructor?
In this example, nothing at all.
In general, when you deal with objects, there is a clear separation of responsibilities between allocation/deallocation and creation/destruction. Memory for the object is reserved first, then the object is created within that memory. Later, the object is destroyed first, and then its memory is released.
When an object has automatic storage duration, its memory is reserved within the calling scope (ie, a local variable in a function/block, a data member of a class, etc), and it is created within that scope's memory. When that scope ends (ie, the function/block ends, the containing object is destroyed, etc), the scoped object is automatically destroyed and its memory is released.
Thus, in your example, the default destructor of Dude
does nothing, since there is nothing for it to destroy.
Dude
has a single int
data member, which is created with automatic storage duration within Dude
. When a Dude
object is created, space for the int
is included in the memory allocation for the Dude
object, but the int
is not valid for use until the Dude
object is constructed within that memory. Later, when the Dude
object is destroyed, the int
becomes invalid for use when the Dude
object's destructor is called, and then the memory for the int
disappears when the memory for the Dude
object is deallocated.
In my first example above, the Dude
object has automatic storage duration. Its scope is main()
, so the compiler reserves memory inside the stack frame of main()
to hold the Dude
object, and then creates the Dude
object (ie, calls its constructor) within that memory. When main()
exits, the Dude
object goes out of scope and is destroyed (ie, its destructor is called), and its memory is released when the stack frame is cleaned up.
In my second example above, the Dude
object has dynamic storage duration. Its scope is dynamic memory. new
allocates dynamic memory to hold the Dude
object, and then creates the Dude
object (ie, calls its constructor) within that memory. Later, delete
destroys the Dude
object (ie, calls its destructor) and deallocates the dynamic memory that new
had allocated.
Note that the int
inside of Dude
always has automatic storage duration. Its scope is Dude
, so it gets allocated wherever a Dude
object is allocated, and is deallocated whenever the Dude
object is deallocated. Whether that be in automatic memory (ie, the stack) or in dynamic memory (ie, the heap).
dude1->~Dude();
What does this piece of code do?
It explicitly calls the object's destructor, like any other method call. However, DO NOT DO THIS, except for objects that have been constructed with placement-new
, eg:
int main(int argc, char* argv[])
{
// FYI, this is not the correct way to ensure the allocated memory
// is satisfactory for creating an object in it, this is simplified
// just for demonstration purposes!
char *buffer = new char[sizeof(Dude)];
Dude *dude1 = new (buffer) Dude(21);
std::cout << dude1->age << '\n';
dude1->~Dude();
delete[] buffer;
return 0;
}