Armen Tsirunyan
Armen Tsirunyan

Reputation: 133122

virtual destructor's practical necessity in a particular case

C++03 5.3.5.3

In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.

This is the theory. The question, however, is a practical one. What if the derived class adds no data members?

struct Base{
   //some members
   //no virtual functions, no virtual destructor
};
struct Derived:Base{
   //no more data members
   //possibly some more nonvirtual member functions
};

int main(){
     Base* p = new Derived;
     delete p; //UB according to the quote above
}

The question: is there any existing implementation on which this would really be dangerous? If so, could you please describe how the internals are implemented in that implementation which makes this code crash/leak or whatever? I beg you to believe, I swear that I have no intentions to rely on this behavior :)

Upvotes: 0

Views: 264

Answers (5)

valdo
valdo

Reputation: 12951

I totally agree with 'Roddy'.

Unless you're writing the code for perverted compiler designed for a non-existing virtual machine just to prove that so-called undefined behavior can bite - there's no problem.

The point of 'sharptooth' about custom new/delete operators is inapplicable here. Because virtual d'tor and won't solve in any way the problem he/she describes.

However it's a good point though. It means that the model where you provide a virtual d'tor and by such enable the polymorphic object creating/deletion is defective by design. A more correct design is to equip such objects with a virtual function that does two things at once: call its (correct) destructor, and also free its memory the way it should be freed. In simple words - destroy the object by the appropriate means, which are known for the object itself.

Upvotes: 0

mukeshkumar
mukeshkumar

Reputation: 2778

In your particular case, where you do not have any data member declared in the derived class and if you do not have any custom new/delete operators (as mentioned by Sharptooth), you may not have any problems ,but do you guarantee that no user will ever derive your class? If you do not make your Base's destructor virtual, there is no way for any of the classes derived from Derived to call their destructors in case the objects of derived classes are used via a Base pointer.

Also, there is a general notion that if you have virtual functions in your base class, the destructor should be made virtual. So better not surprise anybody :)

Upvotes: 0

Roddy
Roddy

Reputation: 68114

I know of no implementation on which the above would be dangerous, and I think it unlikely that there ever will be such an implementation.

Here's why:

"undefined behaviour" is a catch-all phrase meaning (as everyone knows), anything could happen. The code could eat your lunch, or do nothing at all.

However, compiler writers are sane people, and there's a difference between undefined behaviour at compile-time, and undefined behaviour at run-time. If I was writing a compiler for an implementation where the code snippet above was dangerous, it would be easy to catch and prevent at compile time. I can says it's a compilation error (or warning, maybe): Error 666: Cannot derive from class with non-virtual destructor.

I think I'm allowed to do that, because the compiler's behaviour in this case is not defined by the standard.

Upvotes: 1

CashCow
CashCow

Reputation: 31455

I can't answer for specific compilers, you'd have to ask the compiler writers. Even if a compiler works now, it might not do so in the next version so I would not rely on it.

Do you need this behaviour?

Let me guess that

  1. You want to be able to have a base class pointer without seeing the derived class and
  2. Not have a v-table in Base and
  3. Be able to clean up in the base class pointer.

If those are your requirements it is possible to do, with boost::shared_ptr or your own adaptation.

At the point you pass the pointer you pass in a boost::shared_ptr with an actual "Derived" underneath. When it is deleted it will use the destructor that was created when the pointer was created which uses the correct delete. You should probably give Base a protected destructor though to be safe.

Note that there still is a v-table but it is in the shared pointer deleter base not in the class itself.

To create your own adaptation, if you use boost::function and boost::bind you don't need a v-table at all. You just get your boost::bind to wrap the underlying Derived* and the function calls delete on it.

Upvotes: 0

sharptooth
sharptooth

Reputation: 170569

One example is if you provide a custom operator new in struct Derived. Obviously calling wrong operator delete will likely produce devastating results.

Upvotes: 6

Related Questions