2

In the code below I call the ~destructor() explicitly. However the object is still accessible. How can I delete it(make it disappear)?

class Queue {
    public:
    node* top = NULL;//points to the top of the queue 
    //methods:
    void enqueue(int data);//adds a node to the queue
    void dequeue();
    //printing 
    void print();
    //destructor 
    ~Queue();
};

And the destructor:

Queue::~Queue() {
    //The destructor deletes all items from HEAP
    //Then sets the top to 0
    while (top != NULL)
        this->dequeue();//dequeue until there are NO more items
    top = 0;
}

In Source.cpp:

Queue q;

q.enqueue(1);
q.enqueue(2);
q.enqueue(3);
q.enqueue(4);

q.dequeue();
q.dequeue();
q.print();

q.~Queue();
q.print();//Here I need to have an ERROR!

q.enqueue(7);//Here I need to have an ERROR!
q.print();//Here I need to have an ERROR!

The output is:

4 3 7

I expect an error:

identifier "q" is undefined

Jonathan Willcock
  • 5,012
  • 3
  • 20
  • 31
Barbu Vlad
  • 31
  • 3
  • 2
    There's rarely a good reason to call destructor functions explicitely. What you're doing is just _undefined behavior_, you can't expect such error message. – πάντα ῥεῖ Apr 12 '19 at 19:29
  • Calling a destructor does not remove the identifier. Accessing a destroyed object invokes *undefined behavior* – UnholySheep Apr 12 '19 at 19:30
  • `q` iis still there. All you did was call the destructor on it. Even if `q` wasn't still there you may still be able to access it. The results of invoking undefined behaviour are undefined. – user4581301 Apr 12 '19 at 19:30
  • reality is the otherway around: You are not supposed to access the object after it has been destroyed – 463035818_is_not_an_ai Apr 12 '19 at 19:30
  • You must not expect obvious diagnostics for all possible errors in c++. Calling a non-`static` member function on a destroyed object is undefined behavior and, practically speaking, there is no way to guard against it the way you are asking. The fact that local objects' scope ends at the same time as they are destroyed is the way the language guards against it, and by calling the destructor manually you are going out of your way to circumvent that protection. – François Andrieux Apr 12 '19 at 19:30
  • Why are you calling the destructor explicitly? – David Hoelzer Apr 12 '19 at 19:32
  • Put the identifier inside a block. At the end of the block, when the identifier goes out of scope, the destructor will be called. And when the identifier goes out of scope it no longer exists, so there is no way you can use it. Don’t call destructors. That’s an advanced feature, for experts only. – Pete Becker Apr 12 '19 at 19:33
  • 1
    Is this an [XY issue](http://xyproblem.info/)?. Is your goal to remove all the items? If so, then you should have had a `clear()` member function for that purpose instead of trying to erroneously call the destructor to do that. – PaulMcKenzie Apr 12 '19 at 19:53

1 Answers1

5

Taking //Here I need to have an ERROR! literally, here is how you would do that:

{
    Queue q;

    q.enqueue(1);
    q.enqueue(2);

    q.dequeue();
    q.print();
}
q.print();        // THIS WILL PRODUCE AN ERROR

You seem to have a misunderstanding on lifetime of stack allocated objects. Stack objects are automatically destroyed when they go out of scope. In the example the scope of q ends with }.

Calling the destructor yourself is almost always wrong (I encountered exactly one single case where it was ok to call the destructor explicitly). Why? Consider this:

{
    Queue q;
    q.~Queue();   // DONT DO THIS !
}

You called the destructor but when it goes out of scope it gets destroyed again and you will get nasty runtime errors.

What you do in your code:

Queue q;
q.~Queue();
q.print();

is undefined behaviour!

Also note that calling the destructor is not all that happens when an object is deleted. When an stack allocated object is deleted, first its destructor is called and then the allocated memory is freed. Usually you do not want to interfer with this process and luckily you rarely have to.

How can I delete it(make it disappear)?

You cannot make it "disappear". When an object is destroyed the bits and bytes in memory are not erased. That would be awfully inefficient. Actually I think C's free has a much better and less confusing name. Memory is freed to be used later, it is not wiped out such that it would be impossible to read what was there before.

For further reading I refer you to this exahaustive answer to a slightly different but related question: Can a local variable's memory be accessed outside its scope?

Also, I suggest you to read about RAII which relies on destructors being called automatically.

TL;DR: If you want to clear the Queue then write:

q.clear();
q.print(); // prints an empty queue!

Don't ever call the destructor of a stack allocated object! It will be called automatically.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Worth adding a note that calling the destructor on an object does not free the storage for that object. It's just a function. Under normal use (the object is `delete`d or goes out of scope) something else comes along after the destructor runs to free the storage. – user4581301 Apr 12 '19 at 20:16
  • @user4581301 indeed. added a note, but my knowledge doesnt go beyond "something else comes along and does something" so I tried to stay vague ;) feel free to edit if you think you can improve – 463035818_is_not_an_ai Apr 12 '19 at 20:23
  • I'm in the same boat. I've seen how it's implemented in some runtimes, but haven't delved into that part of the standard to see what is and isn't required. Too far off the beaten path for the kind of work I do to have run into it. – user4581301 Apr 12 '19 at 20:27