Topic: I have a non-virtual destructor and...


Author: James.Kanze@dresdner-bank.com
Date: 1999/03/03
Raw View
In article <36DA16C0.831909FD@nonexistant.com>,
  "Robert O'Dowd" <nospam@nonexistant.com> wrote:
>
> Andrei Alexandrescu wrote:
> >
> > Hi,
> >
> > Here's the code:
> >
> > struct A
> > {
> >     virtual void foo() = 0;
> >     // warning: no virtual destructor
> > };
> >
> > struct B : public A
> > {
> >     int i;
> >     double j;
> >     // ... other primitive types here ...
> >     virtual void foo()
> >     {
> >     }
> >     // no destructor explicitly defined
> > };
> >
> > int main()
> > {
> >     auto_ptr<A> spA(new B);
> > }
> >
> > Here's the question: do I have to make A's destructor virtual in order
> > for the above code to be well-defined? I stress that I have *only*
> > primitive types in B and I don't do *absolutely nothing* in its
> > destructor. Guaranteed.
> >
>
> As is, the code does behave in a well defined manner.

The code invokes undefined behavior.  It is not well defined at all.

> The problem is
> that well defined manner may not be what you want.
>
> When spA passes out of scope, its destructor will be invoked.  That
> will, in turn, delete the pointer provided by "new B".  Since class
> A does not provide a virtual destructor, only the destructor of
> class A will be invoked.

Since the destructor is not virtual, and the static type of delete is
*not* the type allocated, undefined behavior is invoked.  It has nothing
to do with which destructor gets called (if any).  It is entirely
conceivable that the wrong address will be passed to the operator delete
function, which in turn may lead to corrupting arbitrary memory; on some
systems, it could even modify the OS so that the next read operation
reformatted the hard disk.

--
James Kanze                                           GABI Software, S   rl
Conseils en informatique orient    objet  --
                          --  Beratung in industrieller Datenverarbeitung
mailto: kanze@gabi-soft.fr          mailto: James.Kanze@dresdner-bank.com

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Robert O'Dowd" <nospam@nonexistant.com>
Date: 1999/03/01
Raw View
Andrei Alexandrescu wrote:
>
> Hi,
>
> Here's the code:
>
> struct A
> {
>     virtual void foo() = 0;
>     // warning: no virtual destructor
> };
>
> struct B : public A
> {
>     int i;
>     double j;
>     // ... other primitive types here ...
>     virtual void foo()
>     {
>     }
>     // no destructor explicitly defined
> };
>
> int main()
> {
>     auto_ptr<A> spA(new B);
> }
>
> Here's the question: do I have to make A's destructor virtual in order
> for the above code to be well-defined? I stress that I have *only*
> primitive types in B and I don't do *absolutely nothing* in its
> destructor. Guaranteed.
>

As is, the code does behave in a well defined manner.  The problem is
that well defined manner may not be what you want.

When spA passes out of scope, its destructor will be invoked.  That
will, in turn, delete the pointer provided by "new B".  Since class
A does not provide a virtual destructor, only the destructor of
class A will be invoked.  In this case, since you haven't declared
a destructor, the compiler generated destructor will be invoked.

The destructor of class B, whether compiler generated or not, will
never be invoked.

The main concern I have with this code is what will happen (say)
if I do the following.

struct C : public A
{
   C() {c = new double[200];};
   ~C() {delete [] c;};
  private:
      double *c;
};

You have no real way of stopping me doing this, without tricks
like making all constructors of class A private.  So, I will
encounter a leak in the following.

main()
{
    auto_ptr<A> spA(new C);
}

In itself, that would discourage me strongly from using your
class A as a base for anything.  Providing a virtual member
function is usually a sign that I can derive safely from your
class or struct.  By not providing a virtual destructor, you
are limiting reuse of your class by providing a timebomb that
I'll trip over in future.  If I was interested in buying your
class library, you will have lost a customer.


-<Automagically included trailer>
Robert O'Dowd                       Ph    +61 (8) 8259 6546
MOD/DSTO                     Fax    +61 (8) 8259 5139
P.O. Box 1500                       Email:
robert.odowd@dsto.defence.gov.au
Salisbury, South Australia, 5108

Disclaimer: Opinions above are mine and may be worth what you paid for
them


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Andrei Alexandrescu" <alexandrescua@micromodeling.com>
Date: 1999/03/01
Raw View
Robert O'Dowd wrote in message <36DA16C0.831909FD@nonexistant.com>...
>As is, the code does behave in a well defined manner.  The problem is
>that well defined manner may not be what you want.


What you said is wrong according to what Fergus Henderson quoted from
the Standard.

>You have no real way of stopping me doing this, without tricks
>like making all constructors of class A private.  So, I will


Really? My class is private in another class. That's why I could make
such strong guarantees.

Andrei




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Andrei Alexandrescu" <alexandrescua@micromodeling.com>
Date: 1999/02/24
Raw View
Hi,

Here's the code:

struct A
{
    virtual void foo() = 0;
    // warning: no virtual destructor
};

struct B : public A
{
    int i;
    double j;
    // ... other primitive types here ...
    virtual void foo()
    {
    }
    // no destructor explicitly defined
};

int main()
{
    auto_ptr<A> spA(new B);
}

Here's the question: do I have to make A's destructor virtual in order
for the above code to be well-defined? I stress that I have *only*
primitive types in B and I don't do *absolutely nothing* in its
destructor. Guaranteed.

Andrei
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: fjh@cs.mu.OZ.AU (Fergus Henderson)
Date: 1999/02/24
Raw View
"Andrei Alexandrescu" <alexandrescua@micromodeling.com> writes:

> struct A {
>     virtual void foo() = 0;
>     // warning: no virtual destructor
> };
>
> struct B : public A { ... }
>
> int main() {
>     auto_ptr<A> spA(new B);
> }
>
> Here's the question: do I have to make A's destructor virtual in order
> for the above code to be well-defined? I stress that I have *only*
> primitive types in B and I don't do *absolutely nothing* in its
> destructor. Guaranteed.

According to the standard, yes, you need to make A's destructor virtual.
The destructor for auto_ptr<A> will call delete on a pointer whose
static type is A, but which actually points to an object of type B,
thus violating 5.3.5 paragraph 3:

 |  5.3.5 - Delete [expr.delete]
 ...
 |  -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.

In practice, it will nevertheless work fine on most implementations.

--
Fergus Henderson <fjh@cs.mu.oz.au>  |  "Binaries may die
WWW: <http://www.cs.mu.oz.au/~fjh>  |   but source code lives forever"
PGP: finger fjh@128.250.37.3        |     -- leaked Microsoft memo.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]