Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations bkrike on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Virtual destructor 1

Status
Not open for further replies.

globos

Programmer
Nov 8, 2000
260
FR
Hi,

Maybe it's written in every C++ book or C++ tutorial, but thanks to a bug, I have found a strange behaviour of C++ classes.
When defining in a class a virtual destructor, it will be automatically called when its heirs' destructor are called.
Example :

Code:
class C
{
public:
  virtual ~C ()
  {
    cout << &quot;C desctructor&quot; << endl;
  }
  virtual void foo ()
  {
    cout << &quot;C foo&quot; << endl;
  }
};

class D : public C
{
public:
  ~D ()
  {
    cout << &quot;D desctructor&quot; << endl;
  }
  void foo ()
  {
    cout << &quot;D foo&quot; << endl;
  }
};

int main ()
{
  D d;
  d.foo ();
  return 0;
}
When executing this program, you get :
Code:
  D foo
  D destructor
  C destructor

But in virtual features(excluding destructor), the base feature is not automatically called, you have to do it manually, and that's normal, because you need flexibility when you redefine a feature. But don't call the parent's destructor in the destructor of a class, because it will be called twice!
So be careful with that!!


--
Globos
 
This ensures that a class' destrucor will ALWAYS be called, which is quite essential.

>So be careful with that!!

Well, if the dtor wasnt called automagically you'd certainly need to be even more careful...



/Per

&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;
 
Regarding calling a destructor twice: This should not be a problem really since a destructor must always succeed no matter what (a destructor may never fail/throw exception).

Note that &quot;always&quot; also includes situations where its been called already.

This is for example the reason to assign deleted pointer to NULL in the destructor - it ensures its no problem to call it again.



/Per

&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;
 
You are perfectly right.
The i was talking about was :
-call destructor twice
-and then try to delete data already deleted(pointers not null assigned)

But if a class is using references for some its attributes, in the desctructor you would write, for example :
Code:
  delete& my_ref_attr;

How can you assign a null reference to my_ref_attr then?
Maybe doing this :
Code:
   const T* pref_attr = &my_ref_attr;
   delete& my_ref_attr;
   pref_attr = NULL;
or :
Code:
   delete& my_ref_attr;
   my_ref_attr = *static_cast<T*> (NULL);

I did not test these code snippet, but the second seems suspicious to me. And the first one too.

--
Globos
 
>delete data already deleted(pointers not null assigned)
This would be the bug you mentioned.

>But if a class is using references for some its attributes

A class that uses references? Well, I would interpret that as the class is infact NOT the owner and consequently not the one to delete.

>How can you assign a null reference to my_ref_attr then?

That is something you should NEVER do.

An attempt to destroy a reference would indicate a severe error:
1) Either you are not the owner - then you should not delete.
2) You are infact the owner - then it should not be a reference.




/Per

&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;
 
OK,
I looked for semantics on the web about what you told, and I came on the C++ pointer adoption principle. It's a good rule to differentiate semantics of C++ reference against pointer.
I should make my classes compliant to this principle.

Thanks.

--
Globos
 
>It's a good rule to differentiate semantics of C++ reference against pointer.

Indeed. There's more to it than just using a -> or a dot :)

On the subject:
Scott Meyer begins his (excellent) &quot;More Effective C++&quot; (sequel to the also excellent &quot;Effective C++&quot;) with :
Item 1 - Distinguish between pointers and references.

Also see thread116-617954

/Per

&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;
 
I come on conflictual semantics problems when considering this &quot;C++ pointer adoption principle&quot;.

For example :
Code:
void AClass::foo (BarClass* bar)
{
   ...
}

This means that AClass::foo is responsible of the deletion of its formal parameter 'bar'.
But pointers are used also to model 0..1 relationships.
So you may want to use a pointer to model this but it means then also that it has to manage deletion, which you would like not to intend.
How to have this clean separation then?


--
Globos
 
>This means that AClass::foo is responsible of the deletion of its formal parameter 'bar'

I theory yes, but that is usually not the intent.

>So you may want to use a pointer to model this but it means then also that it has to manage deletion
>How to have this clean separation then?

To separate you can use
1) std::auto_ptr to denote ownership transfer (ie &quot;I am given this object and shall delete it when I'm finished&quot;)

2) pointer to denote 0..1 relationship without ownership transfer.

Unfortunately with 2) you have no fail-safe protection agains deleting (unless you for example make the destructor protected and only accessible to those who may delete, but such dependancies are usually not healthy). This is the core-problem of dealing with pointers.

But you could still use it to clarify the intended use if you know you'd otherwise use auto_ptr.

My way of dealing with 0..1 relationships (without ownership) is simply to explicitly comment it in the destructor.
Code:
Foo::~Foo()
{
  // Intentionally no delete
  // Foo did not new it so its not Foo's to delete.
  mBar = 0;
}
A bit crude perhaps...but it works :)


/Per

&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;
 
Of course, if possible you could pass the pointer as a const, then the client can't delete it. But then you couldn't call any non-const methods on it, so it depends on the situation.

(I always work with the mind-set that casting away const is not an option)

/Per

&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top