tags:

views:

117

answers:

6

I am currently studying COM and the following code confused me.

STDMETHODIMP _(ULONG) ComCar::Release()
{
  if(--m_refCount==0)
    delete this; // how could this "suicide" deletion be possible?
  return m_refCount;
}

I am wondering how could it be possible to delete an object instance within its member method? So I made the following experiment:

class A
{
public:
    void Suicide(void);
    void Echo(void);
    char name;
};

void A::Echo(void)
{
    ::printf("echo = %c\n",name);
}

void A::Suicide(void)
{
    delete this;
}

int main(void)
{
    A a;
    a.name='a';
    a.Suicide(); //failed
}

And the execution does failed at a.Suicide(). The debug report some "Debug Assertion Failed". Could someone shed some light on me? Cause I am totally a newbie on COM.

A related thread is here: http://stackoverflow.com/questions/2308657/question-about-com-release-method

+10  A: 

Change your main's body to:

A* a = new A();
a->name='a';
a->Sucide();

You can only delete what was built by new, of course -- it makes no difference if that delete is in a member function or elsewhere.

Alex Martelli
+7  A: 

In your example, Suicide() fails because it's calling delete on an object that hasn't been dynamically allocated, which is invalid whether or not the calling function is a member.

Member functions can delete this - if they know the this pointer has been dynamically allocated (via new). However, they can't access members after that point, so strictly speaking the example you gave:

STDMETHODIMP _(ULONG) ComCar::Release()
{
  if(--m_refCount==0)
    delete this; // how could this "sucide" deletion be possible?
  return m_refCount;
}

results in undefined behavior at the return statement.

Michael Burr
And the simple fix is just to return 0, of course.
GMan
Thanks, Michael, since you mentioned "dynamical alloacation", I am guessing if there is some opposite "static alloacation"? what is it and what's the difference? maybe its hard to explain it in a word. Could you give me some reference for further study? many thanks. :D
smwikipedia
@GMan: Just want to clarify you mean: `if (--m_refCount==0) { delete this; return 0; } return m_refCount;`. You can't just change the return statement to `return 0;`.
Dan
@Dan: I got it. :D
smwikipedia
@smwikipedia: there are 3 types of "storage duration". Static (such as global variables), Automatic (local variables, like `a` in your example), and Dynamic (allocated via `operator new()`). See http://stackoverflow.com/questions/408670/stack-static-and-heap-in-c for a discussion.
Michael Burr
+1  A: 

Use new to allocate new object of class which your going to destroy by calling delete.

Dave18
+2  A: 

You cannot delete an object that was not dynamically allocated. COM objects are dynamically allocated.

This works:

#include <stdio.h>

class A
{
public:
    void Sucide(void);
    void Echo(void);
    char name;
};

void A::Echo(void)
{
    ::printf("echo = %c\n",name);
}

void A::Sucide(void)
{
    delete this;
}

void main(void)
{
    A *a = new A;
    a->name='a';
    a->Sucide(); // works
}
sergiom
+3  A: 

delete this is valid only when the object was allocated using the new operator. For COM reference counting, this is not unusual.

However, there is another caveat: accessing member variables after delete this is undefined, because the memory for the object has already been returned to the free store. The first code sample you posted does this. To fix it, use a local variable:

STDMETHODIMP_(ULONG) ComCar::Release()
{
  ULONG refCount = --m_refCount;
  if(refCount==0)
    delete this;
  return refCount;
}
bk1e
I personally find it a bit messier, with the local variable. If you know it's zero inside the if-statement, just return 0.
GMan
@GMan, but then you need two return statements, one inside the `if` and one outside -- bk1e's approach saves that duplication!
Alex Martelli
Instead of duplicating the return statement we duplicate the value \*shrug\* Perhaps this one is cleaner.
GMan
+1  A: 

There is a simple reason for this. new and delete must match.

So if you create an object in an dll and handle it to another part (exe, dll) C runtime might be different. In this case you can not call delete because the runtime has no knowlegde about the pointer you want to delete. It might crash.

Because of this its a good design to integrate the suicide method. In Com its a pair of methods.

AddRef
Release

which means that the pointer has a counter to remember how many owner an object has. Only if the last owner calls delete the object is really deleted.

But I think there is an error in the implementation you posted.

return m_refCount;

should not be possible when the object is deleted. At least the behavior is undefined. I think you need to store m_refCount on an local variable to return it in delete case.

STDMETHODIMP _(ULONG) ComCar::Release()
{
  if(--m_refCount==0) {
    delete this; // how could this "sucide" deletion be possible?
    return 0;
  }
  return m_refCount;
}
Totonga