views:

61

answers:

5

Am I allowed to indirectly destroy object from within object's own virtual method? Is it a "defined behavior" (as long as I'm not trying to access anything after destroying the object)?
Example:

#include <memory>
#include <stdio.h>

using std::tr1::shared_ptr;

struct Child{
    virtual void selfdestruct() = 0;
    virtual ~Child(){
        fprintf(stderr, "child destroyed\n");

    }
};

typedef shared_ptr<Child> ChildPtr;

struct Parent{
    ChildPtr child;
    void clear(){
        fprintf(stderr, "clear\n");
        child = ChildPtr();
    }   
    Parent();
};

struct DerivedChild: public Child{
    Parent* parent;

    virtual void selfdestruct(){
        fprintf(stderr, "selfdestruct\n");
        if (parent)
            parent->clear();
    }

    DerivedChild(Parent* p)
    :parent(p){
    }
};

Parent::Parent(){
    child = ChildPtr(new DerivedChild(this));
}


int main(int argc, char** argv){
    Parent p;
    p.child->selfdestruct();    
    fprintf(stderr, "child is 0x%08x\n", p.child);
    return 0;
}

Output:

selfdestruct
clear
child destroyed
child is 0x00000000

If this is not a defined behavior, what can I do instead?

+1  A: 

Objects can destroy themselves. I don't see how being a virtual method changes anything.

Potatoswatter
+4  A: 

Well, a virtual method can call delete this. After the call though, NOTHING ELSE THAT TOUCHES THAT OBJECT INSTANCE can be done, or you have invoked undefined behavior. That includes calling other methods (even non virtual methods), accessing any instance variable, or the like.

Your specific code above invokes undefined behavior because the Child object needs a virtual destructor.

However, any type of situation where an object needs to destroy itself is not the best of designs.

Billy ONeal
"Child object needs a virtual destructor." It was a "typo" - I could have sworn that I made it virtual. "However, any type of situation where an object needs to destroy itself is not the best of designs." In my case there is a situation where object may trigger something that would delete it. Which is why I asked the question.
SigTerm
@SigTerm: `delete this` is a red flag, but indirectly disposing of yourself at the end of some complicated procedure isn't quite as bad.
Potatoswatter
+1  A: 

This basically boils down to a delete this; so the answer is yes, it's allowed.

void clear(){
    fprintf(stderr, "clear\n");
    child = ChildPtr();
}   
John Dibling
A: 
struct X{
   X() {f();}
   virtual void f(){delete this;}
};

int main(){
   //X x;        // evil, 'delete this' is an error, because 'new' itself was not done
   //x.f();    

   X *p = new X; // Ok, but 'p' now points to an already deleted memory
   p->f();       // evil, double delete
}

So, self deletion, is always a tricky issue.

However, your question is about self destruction of the object and not necessary deallocation of memory in which it is constructed.

This is safe only when an it is sure that the object has been constructed in a memory location using 'placement new'

struct X{
   X() {f();}
   ~X(){}
   virtual void f(){this->~X();}    // here an object destroys itself, but no memory deallocation is done. This is fine and safe as well.
};

int main(){
   char *p = new char [sizeof(X)];  // assume alignment requirements are met
   X *px = new (p) X();
   delete [] p;
}
Chubsdad
A: 

It's perfectly valid and also common practice, e.g. for implementing ref-counting (see MS COM's IUnknown)

smocoder