(Original note: this question is not the same question as whether it is safe to remove the pointer to the void, although this problem has something to do with the problem mentioned in update 2. Here the question arises as to why the base class gets a different value from this than it received a derived class for the same object, in cases where the derived object invokes the suicide method of the base class, the base class must have a virtual destructor, and the pointer to be deleted must be a pointer-to-base class ", storing it in void * is not a safe way to remove an object from a base class method.)
I have a diamond-shaped multiple inheritance, where my child class has two parents that inherit from the same great parent, this way:
class Grand class Mom : public virtual Grand class Dad : public Grand class Child : Mom, Dad
I wrote Mom and Child , but Grand and Dad are library classes that I did not write (which is why Mom inherits almost from Grand , but Dad does not work).
Mom implements a pure virtual method declared in Grand . Dad no. Consequently, Child also implements the same method (because otherwise the compiler would object that the Dad declaration of this method inherited by Child had no implementation). The Child implementation simply calls the Mom implementation. Here's the code (I included the code for Dad and Grand , since this is SSCCE, not the code I got stuck using that relies on library classes that I didn't write):
class Grand { public: virtual void WhoAmI(void) = 0; }; class Mom : public virtual Grand { public: virtual void WhoAmI() { void* momThis = this; }
Note that the getZero method in Child never called.
Performing the execution using the debugger, I see that the address in Child* c is 0x00dcdd08 . Going out in Child::WhoAmI , I see that the address in void* childThis also 0x00dcdd08 , and this is what I expect. Coming further to Mom::WhoAmI , I see that void* momThis is assigned 0x00dcdd0c , which I interpret as the address of the Mom subject of my many-inherited Child object (but I admit that I go a little out of my depth at this point).
Well, the fact that Child this and Mom this different doesn't shock me. Here's what it does: if I uncomment the getZero in Mom and go through all this again, Mom::this and Child::this will be the same!
How does adding virtual int getZero() = 0 to the Mom class result in a Mom subobject and a Child object that has the same address? I thought that perhaps the compiler recognized that all the Mom methods were virtual and the vtable was the same as the Child , so they somehow became the βsameβ object, but they added more and different methods for each class . Do not modify this behavior.
Can someone help me understand what determines when this same for the parent and child of the multiple inherited child and when it is different?
Update
I have tried to simplify everything in order to focus as narrowly as I can on the issue of when this has a different meaning in the parent than this parent. To do this, I changed the inheritance to make it a true diamond, with Dad and Mom both inheriting almost from Grand . I excluded all virtual methods and no longer need to specify which method of the parent class I'm calling. Instead, I have a unique method in each parent class that will allow me to use a debugger to find out what this value has in each parent object. I see that this same for one parent and child, but for the other parent it is different. Also, which parent has a different value when the order of the parents changes in the declaration of the child class.
This is disastrous if any of the parent objects tries to delete itself. Here is the code that works fine on my machine:
class Grand { }; class Mom : public virtual Grand { public: void WhosYourMommy() { void* momIam = this;
However, if I change the class Child : Dad, Mom to class Child : Mom, Dad , it crashes at runtime:
class Grand { }; class Mom : public virtual Grand { public: void WhosYourMommy() { void* momIam = this;
This is a problem when you have a class that includes methods that can delete objects of this class (the "suicide method"), and these methods can be called from derived classes.
But I think I found a solution: any base class that includes a method that can delete instances of itself and that can have these methods, called from instances of classes derived from this class, must have a virtual destructor.
Adding one of the code above will crash:
class Grand { }; class Mom : public virtual Grand { public: void WhosYourMommy() { void* momIam = this;
A lot of people I met have the overwhelming idea that the object is deleted by itself, but it is legal and a necessary idiom when implementing the COM IUnknown :: Release method. I found good recommendations on how to safely use delete this , and some equally good recommendations on using virtual destructors to solve this problem.
I note, however, that if the person who encoded your parent class encoded it using a virtual destructor, calling any suicide method of that parent class from an instance of the class derived from that parent is likely to break and is so unpredictable. Perhaps the reason for enabling virtual destructors is even if you don't think you need it.
Update 2
Well, the problem will return if you add a virtual destructor to Dad and Mom . This code fails when trying to remove the Dad this pointer that does not match the Child this pointer:
class Grand { }; class Mom : public virtual Grand { public: virtual ~Mom() {}; void WhosYourMommy() { void* momIam = this;
Update 3
Thanks to BeyelerStudios for asking the right question: removing void* instead of deleting Dad* prevented C ++ from knowing what it really removes, and thus prevented it from delete dadIam virtual destructors of the base and derived classes, Replacing delete dadIam with delete this solves this problem and the code works fine.
Although this would be somewhat ridiculous, replacing delete dadIam with delete (Dad*)dadIam also great and helps illustrate that the type of pointer that delete works on is important for what delete does. (Something I hardly should be surprised at a polymorphic language.)
BeyelerStudios, if you want to post this as an answer, I will check the box for you.
Thanks!