As the delete operator deallocates memory, why do I need a destructor?
Asked Answered
A

9

7

From c++ FAQ: http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.9

Remember: delete p does two things: it calls the destructor and it deallocates the memory.

If delete deallocates the memory, then what's the need of the destructor here?

Aciniform answered 3/2, 2012 at 7:43 Comment(8)
This is the garbage collection fallacy: memory is not the only resource that needs cleanup.Pooch
@R.MartinhoFernandes Explaining in detail would be helpful. One liners aren't usually easy to understand.Aciniform
I'm saying that if delete only released the memory, it wouldn't cleanup other resources, like closing files, or network connections, or releasing locks.Pooch
@AnishaKaul And imagine the destructor of std::shared_ptr: it has to conditionally delete the held pointer and decrease a refcount (possibly in thread safe fashion)Appall
Before a man dies, he may want to sell his car, break up with his girlfriend and cancel his subscription to Wired Magazine. The recipe describing all of those necessary steps before death is called the "destructor". Note the before part. The destructor does not include the demise of the person itself, because no sane human being would state "Before death gets his eternal grip on my soul, I want to make sure I die first."Gambeson
@FredOverflow That's a nice one. But in a destructor, we need to do extra work "ourselves"? right?Aciniform
Memory allocation and object construction are two separate and distinct concepts in C++. Deallocations pertains to the former, destruction to the latter.Neelon
If simple memberwise destruction does the right thing (if your members are std::string and std::vector and such), you don't need to write a destructor yourself. If it doesn't do the right thing (if your members are char* or FILE* or whatever), you must write a destructor doing the right thing. (Note that destructing a pointer does nothing.) So if you want to execute delete member_pointer before an object dies, you have to explicitly say so in the destructor. See this related FAQ for details.Gambeson
B
10

You need to call the destructor in case there are other things that need to be done other than just de-allocating memory.

Other than very simple classes, there usually are.

Things like closing file handles or shutting down database connections, deleting other objects that are pointed to by members data within your object, and so forth.

A classic example is the implementation of a stack:

class myStack {
    private:
        int *stackData;
        int topOfStack;

    public:
        void myStack () {
            topOfStack = 0;
            stackData = new int[100];
        }

        void ~myStack () {
            delete [] stackData;
        }

        // Other stuff here like pop(), push() and so on.
}

Now think of what would happen if the destructor was not called every time one of your stacks got deleted. There is no automatic garbage collection in C++ in this case so the stackData memory would leak and you'd eventually run out.


This requiring of a destructor to delete all its resources moves down the tree towards the basic types. For example, you may have a database connection pool with an array of database connections. The destructor for that would delete each individual database connection.

A single database connection may allocate a lot of stuff, such as data buffers, caches, compiled SQL queries and so on. So a destructor for the database connection would also have to delete all those things.

In other words, you have something like:

+-------------------------------------+
| DB connection pool                  |
|                                     |
| +-------------------------+---+---+ |
| | Array of DB connections |   |   | |
| +-------------------------+---+---+ |
|                             |   |   |
+-----------------------------|---|---+
                              |   |   +---------+
                              |   +-> | DB Conn |
             +---------+      |       +---------+
             | DB Conn | <----+         /  |  \
             +---------+         buffers   |   queries
               /  |  \                  caches
        buffers   |   queries
               caches

Freeing the memory for the DB connection pool would not affect the existence of the individual DB connection or the other objects pointed to by them.

That's why I mentioned that only simple classes can get away without a destructor, and those are the classes that tend to show up at the bottom of that tree above.

A class like:

class intWrapper {
    private:
        int value;
    public:
        intWrapper () { value = 0; }
        ~intWrapper() {}
        void setValue (int newval) { value = newval; }
        int getValue (void) { return value; }
}

has no real need for a destructor since the memory deallocation is all you need to do.


The bottom line is that new and delete are opposite ends of the same pole. Calling new first allocates the memory then calls the relevant constructor code to get your object in a workable state.

Then, when you're done, delete calls the destructor to "tear down" your object the reclaims the memory allocated for that object.

Balneal answered 3/2, 2012 at 7:47 Comment(8)
so the stackData memory would leak and you'd eventually run out. Didn't understand this. Delete deallocates the memory, so calling delete on a stack would deallocate it, so how are the memory leaks happening?Aciniform
@Anisha, a destructor is totally responsible for cleaning up after itself. That means it must also delete any dynamically created objects that it has created during its lifetime (usually, but not necessarily always, in its constructor). The acto of deleting its resources may further move down the tree. See my updated answer. Freeing the memory for a myStack object does not free any memory it has allocated, its destructor is responsible for that.Balneal
So, destructor cleans up the allocated children of the destroyed objects?Aciniform
@Anisha, yes, although there are other things (than simply calling delete) that it may need to do as part of that cleanup (like closing down legacy C libraries that don't use objects).Balneal
has no real need for a destructor since the memory deallocation is all you need to do. In that class, you didn't allocate any memory, so we don't we to deallocate anything. The destructor will still be needed for destroying the object intWrapper a;? Right?Aciniform
No, you don't need a destructor, C++ will simply deallocate the memory - it implicitly supplies a destructor that does nothing if you don't provide your own. You need to understand that memory allocation/deallocation and construction/destruction are two different things, just that new and delete use them both. For example, delete [] arr will call the destructor once per object in the array ut may only deallocate one block of memory.Balneal
You need to understand that memory allocation/deallocation and construction/destruction are two different things, Well, I truly do NOT understand how they are different things. Ah! Do you mean the construction refers to the manual new and allocation refers to the objects like classX objX;?Aciniform
@Anisha, read the final two paragraphs in my answer carefully: new does two things. The first is to allocate the memory (like C's malloc) for your object, the second is to call your constructor so you can set the object up. At the other end delete also does two things. It calls the destructor so you can tear down your object and then it reclaims the memory (a la C's free). I'm not sure I can explain it any clearer and the comments are filling up so, if it's still not clear, you may want to consider asking a new question just on that aspect.Balneal
S
13

If delete deallocates the memory, then what's the need of the destructor here?

The point of the destructor is to execute any logic required to clean up after your object, for example:

  • calling delete on other objects owned by the object being destroyed
  • properly releasing other resources like database connections; file handles and the like
Southey answered 3/2, 2012 at 7:45 Comment(6)
Ah, so you mean that delete deletes a particular object and calls the destructor which deleted the children of the object deleted by delete?Aciniform
@AnishaKaul: well ... not exactly: the actions are reversed. Delete first calls the destructor (that do what you code to do, like deleting something more, closing a file or whatsoever) THEN gives the object memory back to the system.Destruction
@EmilioGaravaglia If I don't write any code in the destructor, it won't delete the children of the deleted object?Aciniform
@AnishaKaul: yes it doesn't. The concept of "children of the object" is something known to you, not to C++. C++ just knows there are pointers to something else. Whether they have to be delete as well or left at their place (because used also by someone else) is something belonging to your logic, that is unknown to the compiler if you din't specify it. The way to specify it is the destructor code. If you want you can think to destructors as "event handlers" that are triggered upon delete.Destruction
@EmilioGaravaglia Wow, so is the destructor of any use after delete? we anyway hhave to write the code ourselves, so we can write it whereever we want?Aciniform
@Anisha: If your object manages heap-allocated objects (the ones created with new), and you want them to be deleted together with your object, you have to delete them in your destructor. See paxdiablos answer.Newspaperwoman
B
10

You need to call the destructor in case there are other things that need to be done other than just de-allocating memory.

Other than very simple classes, there usually are.

Things like closing file handles or shutting down database connections, deleting other objects that are pointed to by members data within your object, and so forth.

A classic example is the implementation of a stack:

class myStack {
    private:
        int *stackData;
        int topOfStack;

    public:
        void myStack () {
            topOfStack = 0;
            stackData = new int[100];
        }

        void ~myStack () {
            delete [] stackData;
        }

        // Other stuff here like pop(), push() and so on.
}

Now think of what would happen if the destructor was not called every time one of your stacks got deleted. There is no automatic garbage collection in C++ in this case so the stackData memory would leak and you'd eventually run out.


This requiring of a destructor to delete all its resources moves down the tree towards the basic types. For example, you may have a database connection pool with an array of database connections. The destructor for that would delete each individual database connection.

A single database connection may allocate a lot of stuff, such as data buffers, caches, compiled SQL queries and so on. So a destructor for the database connection would also have to delete all those things.

In other words, you have something like:

+-------------------------------------+
| DB connection pool                  |
|                                     |
| +-------------------------+---+---+ |
| | Array of DB connections |   |   | |
| +-------------------------+---+---+ |
|                             |   |   |
+-----------------------------|---|---+
                              |   |   +---------+
                              |   +-> | DB Conn |
             +---------+      |       +---------+
             | DB Conn | <----+         /  |  \
             +---------+         buffers   |   queries
               /  |  \                  caches
        buffers   |   queries
               caches

Freeing the memory for the DB connection pool would not affect the existence of the individual DB connection or the other objects pointed to by them.

That's why I mentioned that only simple classes can get away without a destructor, and those are the classes that tend to show up at the bottom of that tree above.

A class like:

class intWrapper {
    private:
        int value;
    public:
        intWrapper () { value = 0; }
        ~intWrapper() {}
        void setValue (int newval) { value = newval; }
        int getValue (void) { return value; }
}

has no real need for a destructor since the memory deallocation is all you need to do.


The bottom line is that new and delete are opposite ends of the same pole. Calling new first allocates the memory then calls the relevant constructor code to get your object in a workable state.

Then, when you're done, delete calls the destructor to "tear down" your object the reclaims the memory allocated for that object.

Balneal answered 3/2, 2012 at 7:47 Comment(8)
so the stackData memory would leak and you'd eventually run out. Didn't understand this. Delete deallocates the memory, so calling delete on a stack would deallocate it, so how are the memory leaks happening?Aciniform
@Anisha, a destructor is totally responsible for cleaning up after itself. That means it must also delete any dynamically created objects that it has created during its lifetime (usually, but not necessarily always, in its constructor). The acto of deleting its resources may further move down the tree. See my updated answer. Freeing the memory for a myStack object does not free any memory it has allocated, its destructor is responsible for that.Balneal
So, destructor cleans up the allocated children of the destroyed objects?Aciniform
@Anisha, yes, although there are other things (than simply calling delete) that it may need to do as part of that cleanup (like closing down legacy C libraries that don't use objects).Balneal
has no real need for a destructor since the memory deallocation is all you need to do. In that class, you didn't allocate any memory, so we don't we to deallocate anything. The destructor will still be needed for destroying the object intWrapper a;? Right?Aciniform
No, you don't need a destructor, C++ will simply deallocate the memory - it implicitly supplies a destructor that does nothing if you don't provide your own. You need to understand that memory allocation/deallocation and construction/destruction are two different things, just that new and delete use them both. For example, delete [] arr will call the destructor once per object in the array ut may only deallocate one block of memory.Balneal
You need to understand that memory allocation/deallocation and construction/destruction are two different things, Well, I truly do NOT understand how they are different things. Ah! Do you mean the construction refers to the manual new and allocation refers to the objects like classX objX;?Aciniform
@Anisha, read the final two paragraphs in my answer carefully: new does two things. The first is to allocate the memory (like C's malloc) for your object, the second is to call your constructor so you can set the object up. At the other end delete also does two things. It calls the destructor so you can tear down your object and then it reclaims the memory (a la C's free). I'm not sure I can explain it any clearer and the comments are filling up so, if it's still not clear, you may want to consider asking a new question just on that aspect.Balneal
W
3

Suppose you have a class that dynamically allocates memory:

class something {
public:
    something() {
        p = new int;
    }

    ~something() {
        delete p;
    }

    int *p;
};

Now let's dynamically allocate a something object:

something *s = new something();

delete s;

Now, if the delete didn't call the destructor, then s->p would never be freed. So delete has to both call the destructor and then deallocate the memory.

Watts answered 3/2, 2012 at 7:47 Comment(4)
Ah, that's nice. So, Ah, so you mean that delete deletes a particular object and calls the destructor which deleted the children of the object deleted by delete?Aciniform
Does this also mean that after deleting the parent object explicitly there are no chances of memory leaks due to paren't children?Aciniform
For the first question, it calls the destructor first and then deletes the object. Because obviously, if you delete the object first, then how can you destruct it?Watts
For the second: Not necessarily. Deleting the parent object only guarantees that the destructor will be called. If you messed up your constructor then it won't delete correctly but assuming you did make your destructor correctly, then yes, there are no chances of memory leaks.Watts
R
2

The destructor is in charge of freeing resources other than the object's allocated memory. For instance, if the object has a file handle open, the destructor could call fclose on it.

Rosecan answered 3/2, 2012 at 7:47 Comment(1)
Not "object's allocated memory". "Memory allocated FOR the object"Destruction
D
1

It deallocates the memory taken up by that object. However, anything that has been allocated by the object (and owned by that object) needs to be taken care of in the destructor.

Also, in general ... FAQs ... usually not wrong.

Dumdum answered 3/2, 2012 at 7:46 Comment(0)
L
0

if you declare a class normal (not pointer), it automatically calls constructor and call destructor automatically when the program closes. If you declare as pointer, it call the constructor when initializes using new and does not call destructor automatically until you call delete that pointer using delete

Latvian answered 3/2, 2012 at 8:5 Comment(0)
C
0

The destructor is there to clean up the changes that the object constructor and member functions might have done to the program state. That can be anything - remove the object from some global list, close an opened file, free allocated memory, close a database connection, etc.

Cade answered 3/2, 2012 at 8:11 Comment(0)
M
0

The destructor would not have been a mandatory feature. The languages like, C, Java, C# don't have destructors. C++ also can live without it.

Destructor is a special facility provided by C++ (same as Constructor). It's called when an object is "destroyed".

Destroy means, the object scope is officially finished and any reference to that object will be illegal. For example:

A* foo ()
{
  static A obj;  // 'A' is some class
  A *p = &obj;
  return p;
}

In above code, obj is a static data created of type A; foo() returns a reference to that obj which is ok, because obj.~A() is not yet called. Suppose obj is non-static. The code will compile, however, A* returned by foo() is now pointing to a memory location which is no more an A object. Means -> the operation is bad/illegal.

Now, you should be able to distinguish between deallocation of the memory and the destruction of the object. Both are tightly coupled, but there is a thin line.

Also remember that destructor can be called at multiple places:

int bar ()
{
  A obj;
  ...
  return 0; // obj.~A() called here
  ...
  return 1; // obj.~A() called here
  ...
  return 2; // obj.~A() called here
}

In above example, obj.~A() will be called only once, but it can be called from any of the places shown.

During the destruction, you may want to do some useful stuff. Suppose class A calculates some result, when the object destroys; it should print the result of calculation. It can be done in C style way (putting some function at every return statement). But ~A() is a readily available one-stop facility.

Mariannemariano answered 3/2, 2012 at 8:13 Comment(2)
but it can be called from any of the places shown How's that possible? When the return 0 is met, the funtion returns, and the remaining code can NEVER get executed, isn't it?Aciniform
@AnishaKaul, yes that's the point. obj.~A() is called only once, but compiler emits it code at all the above places shown. Because compiler doesn't know that at runtime which return will be executed.Mariannemariano
B
0

In addition to answers focused on the objects allocated on the heap (with new; which are deallocated only with delete)...Don't forget that if you place the object on the stack (so, without using new), its destructor will be called automatically and it will be removed from the stack (without calling delete) when it goes out of scope. So you have one function that is guaranteed to be executed when object goes out of the scope and that is perfect place to perform cleanup of all other resources allocated by the object (various handles, sockets...and objects created on the heap by this object - if they must not outlive this one). This is used in the RAII idiom.

Belonging answered 3/2, 2012 at 8:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.