0

I have the following code and I get stack overflow error can anyone please explain me What's wrong here. from my understanding this pointer points to current object so why I cant delete it in a destructor;

class Object 
{
private:
    static int objCount;
public:
    int getCount()
    {
        int i =10;
        return i++;
    }
    Object()
    {
        cout<< "Obj Created = "<<++objCount<<endl;
        cout <<endl<<this->getCount()<<endl;
    }
    ~Object()
    {
        cout<<"Destructor Called\n"<<"Deleted Obj="<<objCount--<<endl;
        delete this;
    }
};

int Object::objCount = 0;

int _tmain(int argc, _TCHAR* argv[])
{


    {
        Object obj1;
    }

    {

              Object *obj2 = new Object();
    }
    getchar();
    return 0;
}
Krishna Oza
  • 1,390
  • 2
  • 25
  • 50
  • What happens when you try to delete an object which is not allocated using new. i.e. deleting an object on stack instead from heap. Also for object created on heap using new I cant call delete in destructor. – Krishna Oza Jan 24 '14 at 07:02
  • You can't `delete` an object you didn't `new`, the way you can't borrow a book from the library and return it at hospital. Likewise, you can't do this: `int* n = new int[5]; delete n[3];` because `new` allocates a block of memory and turns it into one or more instance of the object you requested by calling constructors. `delete` deletes expects a pointer to a single object, destructs it and returns the memory to the `new` allocator. If the memory came from `new[]` the `new` allocator can't deal with it and bad things happen. Call `delete[]` instead (`int* a = new int[5]; delete [] a;`) – kfsone Jan 24 '14 at 08:15

4 Answers4

15

You're doing delete this; in your class's destructor.

Well, delete calls the class's destructor.

You're doing delete this; in your class's destructor.

...


<!<!<!Stack Overflow!>!>!>

(Sorry guys I feel obliged to include this... this might probably spoil it sorrrryyyy!

Moral of the boring story, don't do delete this; on your destructor (or don't do it at all!)

Do [1]

Object *obj = new Object();
delete obj;

or much better, just

Object obj;

[1]@kfsone's answer more accurately points out that the stack overflow was actually triggered by obj1's destructor.

Community
  • 1
  • 1
Mark Garcia
  • 17,424
  • 4
  • 58
  • 94
3

'delete this' never makes sense. Either you're causing an infinite recursion, as here, or you're deleting an object while it is still in use by somebody else. Just remove it. The object is already being deleted: that's why you're in the destructor.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • "'delete this' never makes sense" is a dogma, not an answer. Do you know there are entire OOP frameworks written around signaling and self deletion? The point -heere- is that `delete this` was called by the dtor! – Emilio Garavaglia Jan 24 '14 at 07:45
  • @EmilioGaravaglia You're welcome to your opinion of my opinion, but don't misrepresent what I wrote. You don't appear to have read anything but the first sentence of my answer. Try reading it all. You will find I have already said everything you now think you're telling me, and you will also find that I have answered the question with a specific coding recommendation. – user207421 Jan 24 '14 at 11:37
  • It is just because I read it all that I expressed that opinion. "never make sense" is false. it "does not makes sense in this case" for the reason we both explained. Be careful with words like "always" or "never": one counterexample is enough to make a theorem false. – Emilio Garavaglia Jan 25 '14 at 16:39
3

The crash you are having is because of the following statement:

{
    Object obj1;
}

This allocates an instance of "Object" on the stack. The scope you created it in ends, so the object goes out of scope, so the destructor (Object::~Object) is invoked.

{
    Object obj1;
    // automatic
    obj1.~Object();
}

This means that the next instruction the application will encounter is

delete this;

There are two problems right here:

  1. delete calls the object's destructor, so your destructor indirectly calls your destructor which indirectly calls your destructor which ...
  2. After the destructor call, delete attempts to return the object's memory to the place where new obtains it from.

By contrast

{

          Object *obj2 = new Object();
}

This creates a stack variable, obj2 which is a pointer. It allocates memory on the heap to store an instance of Object, calls it's default constructor, and stores the address of the new instance in obj2.

Then obj2 goes out of scope and nothing happens. The Object is not released, nor is it's destructor called: C++ does not have automatic garbage collection and does not do anything special when a pointer goes out of scope - it certainly doesn't release the memory.

This is a memory leak.

Rule of thumb: delete calls should be matched with new calls, delete [] with new []. In particular, try to keep new and delete in matching zones of authority. The following is an example of mismatched ownership/authority/responsibility:

auto* x = xFactory();
delete x;

Likewise

auto* y = new Object;
y->makeItStop();

Instead you should prefer

// If you require a function call to allocate it, match a function to release it.
auto* x = xFactory();
xTerminate(x); // ok, I just chose the name for humor value, Dr Who fan.

// If you have to allocate it yourself, you should be responsible for releasing it.
auto* y = new Object;
delete y;

C++ has container classes that will manage object lifetime of pointers for you, see std::shared_ptr, std::unique_ptr.

kfsone
  • 23,617
  • 2
  • 42
  • 74
  • the sandwich method new ... delete does not gurantee that the memory would be released so its always better ot use smart_ptr, shared_ptr or auto_ptr; – Krishna Oza Jan 24 '14 at 08:10
  • can any one explain why the new... delete will fail and the pointers does not. – Krishna Oza Jan 24 '14 at 08:13
  • It seems like you're not understanding how C++ functions works. `delete` calls the destructor of the object you are deleting. If your destructor calls delete on itself (this), then you create an infinite loop - calling delete with the pointer delete just used to call your destructor. `delete` calls the destructor which calls delete, which immediately invokes the Destructor on the object again, which immediately calls delete again, which ... – kfsone Jan 24 '14 at 08:23
  • @krish_oza: when you use `new` and later `delete`, how can you sure *beyond all doubt* that the flow of execution will actually reach `delete` ? (consider exceptions...) – Matthieu M. Jan 24 '14 at 08:24
  • @matthieu-m yes that was I wanted to point out that if in betwwen new and delete some exception is there and if the system couldn't execute the delete than it will lead to memory leak where as if you use smart_ptr they will be deleted during stackunwinding. – Krishna Oza Jan 24 '14 at 09:17
  • @krish_oza I mention smart pointers at the end of the article, however, your *question* was about using `delete this` in a destructor , to quote: "from my understanding this pointer points to current object so why I cant delete it in a destructor;". Even if you use a smart_pointer, *you should not use delete in a destructor* since the smart_pointer - your destructor got called by the smart pointer calling `delete` on your object, on the address to which `this` points inside the destructor which will try to release the same allocation twice. – kfsone Jan 25 '14 at 01:56
  • e.g. If you create a `unique_ptr` to an `Object` allocated by `new` who's address is 0x1234, when the unique_ptr goes out of scope, it's destructor is invoked, and the call sequence will be: `unique_ptr::~unique_ptr()` => `delete (Object*)0x1234` => `0x1234->~Object()` aka `Object::~Object(this=0x1234)` -> `delete (Object*)0x1234` -> `Object::~Object(this=0x1234)` creating an infinite loop, and if the loop breaks, it's going to try and release the memory multiple times. – kfsone Jan 25 '14 at 01:59
1

There are two issues here:

  1. You are using delete, which is generally a code smell
  2. You are using delete this, which has several issues

Guideline: You should not use new and delete.

Rationale:

  • using delete explicitly instead of relying on smart pointers (and automatic cleanup in general) is brittle, not only is the ownership of a raw pointer unclear (are you sure you should be deleting it ?) but it is also unclear whether you actually call delete on every single codepath that needs it, especially in the presence of exceptions => do your sanity (and that of your fellows) a favor, don't use it.

  • using new is also error-prone. First of all, are you sure you need to allocate memory on the heap ? C++ allows to allocate on the stack and the C++ Standard Library has containers (vector, map, ...) so the actual instances where dynamic allocation is necessary are few and far between. Furthermore, as mentioned, if you ever reach for dynamic allocation you should be using smart pointers; in order to avoid subtle order of execution issues it is recommend you use factory functions: make_shared and make_unique (1) to build said smart pointers.

    (1) make_unique is not available in C++11, only in C++14, it can trivially be implemented though (using new, of course :p)


Guideline: You shall not use delete this.

Rationale:

Using delete this means, quite literally, sawing off the branch you are sitting on.

  • The argument to delete should always be a dynamically allocated pointer; therefore should you inadvertently allocate an instance of the object on the stack you are most likely to crash the program.
  • The execution of the method continues past this statement, for example destructors of local objects will be executed. This is like walking on the ghost of the object, don't look down!
  • Should a method containing this statement throw an exception or report an error, it is difficult to appraise whether the object was successfully destroyed or not; and trying again is not an option.

I have seen several example of usage, but none that could not have used a traditional alternative instead.

Community
  • 1
  • 1
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • The second rule might warrant a mention of impact on nested dtors from inheritance/multiple inheritance... – kfsone Jan 25 '14 at 01:04
  • @kfsone: I don't think it would cause *extra* issues so long as the static type of `this` has a `virtual` destructor OR already matches the actual dynamic type. – Matthieu M. Jan 25 '14 at 13:20
  • it depends where it is on the hierarchy: `class A { virtual ~A() { delete this; } }; class B : public A { virtual ~B() { delete this; } }; class C : public B { virtual ~C() { delete this; } };` kaboom :) – kfsone Jan 25 '14 at 20:27
  • @kfsone: Actually it does not. `~Foo() { delete this; };` is wrong-headed because `delete` calls `~Foo` which calls `delete` which... as I said, no *extra* issue is brought on by inheritance. – Matthieu M. Jan 26 '14 at 12:32