Topic: implicitly protected dtor of abstract class


Author: AllanW@my-dejanews.com
Date: 1998/11/14
Raw View
In article <72h14r$lrl$1@news.lth.se>,
  Hans.Olsson@dna.lth.se (Hans Olsson) wrote:
>
> In article <72fjld$jjm$1@nnrp1.dejanews.com>,  <AllanW@my-dejanews.com>
wrote:
>
> >In article <364adc8f.688455216@netnews.hinet.net>,
> >  peihao@ms3.hinet.net (Buck) wrote:
> >> >> Could it be considered into standards whenever an
> >> >>abstrace class is declared
> >>   abstract (Woops)
> >> >> without a dtor, the dtor is implicitly protected ?
>
> >It seems intuitive that the only code that this breaks would be
> >code that is already broken:
> >    class Example1 {
> >    public:
> >        virtual int func0() { return data; }
> >        virtual int func1() = 0;
> >        virtual int func2() = 0;
> >    protected:
> >        int data;
> >    };
> >    class Exam1_Instance1 : public Example1 {
> >        int *buffer;
> >    public:
> >        Exam1_Instance1() {
> >            buffer = new int[2];
> >            buffer[0] = buffer[1] = 0;
> >        }
> >        ~Exam1_Instance1() { delete[] buffer; }
> >        int func1() { return buffer[0]++; }
> >        int func2() { return buffer[1]++; }
> >    }
> >    // ...
> >    Example1 *x = new Exam1_Instance1;
> >    std::cout << (x->func1()+x->func2()) << std::endl;
> >    delete x; // Memory leak
>
> But you don't have to dynamically allocate the object on the heap:
>
> void foo(Example1&y) {std::cout<<(y.func1()+y.func2())<<std::endl;}
> {
>   Exam1_Instance1 x;
>   foo(x);
> };
>
> It is still dangerous, (forbidding heap-allocation might fix it),
> but I would hope that good compilers warn for missing virtual
> destructors even though the construct is not illegal.

You've found a way to use the class, where the error won't bite
you because when the object is deleted it knows the "real" type.
Here's another, almost identical except that we DO allocate it
from the heap:

    void foo(Example1&y) {std::cout<<(y.func1()+y.func2())<<std::endl;}

    void bar()
    {
        Exam1_Instance1 *x = new Exam1_Instance1;
        foo(*x);
        delete x;
    };

So the error won't affect every program. Does that mean it isn't
an error? No. Without at least a comment warning the end user
about this, the code is broken. And the proposed rule about
abstract class destructors would catch the error, without breaking
any "good" code. So it's a good rule.

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== 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: AllanW@my-dejanews.com
Date: 1998/11/13
Raw View
In article <364adc8f.688455216@netnews.hinet.net>,
  peihao@ms3.hinet.net (Buck) wrote:
> >> Could it be considered into standards whenever an
> >>abstrace class is declared
>   abstract (Woops)
> >> without a dtor, the dtor is implicitly protected ?

It seems intuitive that the only code that this breaks would be
code that is already broken:
    class Example1 {
    public:
        virtual int func0() { return data; }
        virtual int func1() = 0;
        virtual int func2() = 0;
    protected:
        int data;
    };
    class Exam1_Instance1 : public Example1 {
        int *buffer;
    public:
        Exam1_Instance1() {
            buffer = new int[2];
            buffer[0] = buffer[1] = 0;
        }
        ~Exam1_Instance1() { delete[] buffer; }
        int func1() { return buffer[0]++; }
        int func2() { return buffer[1]++; }
    }
    // ...
    Example1 *x = new Exam1_Instance1;
    std::cout << (x->func1()+x->func2()) << std::endl;
    delete x; // Memory leak
This code has an error. If it somehow slipped through code
reviews (or no reviews were done), it can't be caught without
extensive memory management debugging. This might not be
done until there are reports from the end users that memory
is being leaked, making it a very expensive bug indeed.

By following the suggestion above, we would catch the same
error at compile-time. A simple fix (add an explicit
destructor, probably virtual, or else change the delete
statement) would be very cheap, compared to the above
scenario.

But the idea isn't without it's down side. What about code
that breaks this rule but has no errors?

    class Example2 {
    public:
        virtual int func0() { return data; }
        virtual int func1() = 0;
        virtual int func2() = 0;
    protected:
        int data;
    };
    class Exam2_Instance1 : public Example2 {
        int b1, b2;
    public:
        Exam1_Instance1() : b1(0), b2(0) {}
        int func1() { return b1++; }
        int func2() { return b2++; }
    }
    // ...
    Example2 *x = new Exam2_Instance1;
    std::cout << (x->func1()+x->func2()) << std::endl;
    delete x; // No problem

Since Exam2_Instance1's destructor is trivial, there is
no memory leak. Therefore, there's no problem with
Example2's destructor being both public and non-virtual.
Some would call this correct code; others would label it
as a benign error. But the suggestion above would always
label it an error, and require the programmer to fix it.

In my opinion, this potential problem is indeed a bug.
Note that just because Exam2_Instance1 has a trivial
destructor doesn't mean that Exam2_Instance2 will, or
that Exam2_Instance1 won't be changed in the future
without any corresponding change to Example2. The
suggested change promotes good programming practices.
The errors caught in working, but buggy, programs would
far outweigh the cost of fixing the programs that do not
have a malignant bug but do exhibit this particular poor
programming style.

Therefore, I support the idea.

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== 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: Hans.Olsson@dna.lth.se (Hans Olsson)
Date: 1998/11/13
Raw View
In article <72fjld$jjm$1@nnrp1.dejanews.com>,  <AllanW@my-dejanews.com> wrote:

>In article <364adc8f.688455216@netnews.hinet.net>,
>  peihao@ms3.hinet.net (Buck) wrote:
>> >> Could it be considered into standards whenever an
>> >>abstrace class is declared
>>   abstract (Woops)
>> >> without a dtor, the dtor is implicitly protected ?

>It seems intuitive that the only code that this breaks would be
>code that is already broken:
>    class Example1 {
>    public:
>        virtual int func0() { return data; }
>        virtual int func1() = 0;
>        virtual int func2() = 0;
>    protected:
>        int data;
>    };
>    class Exam1_Instance1 : public Example1 {
>        int *buffer;
>    public:
>        Exam1_Instance1() {
>            buffer = new int[2];
>            buffer[0] = buffer[1] = 0;
>        }
>        ~Exam1_Instance1() { delete[] buffer; }
>        int func1() { return buffer[0]++; }
>        int func2() { return buffer[1]++; }
>    }
>    // ...
>    Example1 *x = new Exam1_Instance1;
>    std::cout << (x->func1()+x->func2()) << std::endl;
>    delete x; // Memory leak

But you don't have to dynamically allocate the object on the heap:

void foo(Example1&y) {std::cout<<(y.func1()+y.func2())<<std::endl;}
{
  Exam1_Instance1 x;
  foo(x);
};

It is still dangerous, (forbidding heap-allocation might fix it),
but I would hope that good compilers warn for missing virtual
destructors even though the construct is not illegal.

--
// Home page  http://www.dna.lth.se/home/Hans_Olsson/
// Email To..Hans.Olsson@dna.lth.se [Please no junk e-mail]




[ 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: peihao@ms3.hinet.net (Buck)
Date: 1998/11/12
Raw View
On 10 Nov 98 19:11:56 GMT, Pete Becker <petebecker@acm.org> wrote:

>Buck wrote:
>>
>>
>> Could it be considered into standards whenever an abstrace class is declared
                                                                                       abstract (Woops)
>> without a dtor, the dtor is implicitly protected ?
>
>Probably not. For example:
>
>class Complex
>{
>public:
> Complex(double re, double im);
> friend Complex operator + (Complex c1, Complex c2);
> Complex& operator += (Complex c);
> // etc.
>};
>
>int main()
>{
>Complex c(1.0, 1.0); // illegal: no publicly accessible destructor
>return 0;
>}

You're missing the point here.
A abstract class is a class with pure virtual functions.
Complex is simply not the case

FYI:  Complex is a concrete class
---
[ 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: peihao@ms3.hinet.net (Buck)
Date: 1998/11/10
Raw View

When I declare an interface class, it is always done in one of two forms:

//Form1: Allow pointer destruction
class CInterface1
{
 public:
 virtual ~CInterface1() {}
 virtual void func1(int x)  =0;
 virtual int func2() const =0;
};

//Form2: Disallow pointer destruction
class CInterface2
{
 public:
 virtual void func1(int x)  =0;
 virtual int func2() const =0;

 protected:
 ~CInterface2() {}
};

I feel being punished to write more code to prevent
pointer of interface type from being deleted inadvertently.

Could it be considered into standards whenever an abstrace class is declared
without a dtor, the dtor is implicitly protected ?



[ 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: Pete Becker <petebecker@acm.org>
Date: 1998/11/10
Raw View
Buck wrote:
>
>
> Could it be considered into standards whenever an abstrace class is declared
> without a dtor, the dtor is implicitly protected ?

Probably not. For example:

class Complex
{
public:
 Complex(double re, double im);
 friend Complex operator + (Complex c1, Complex c2);
 Complex& operator += (Complex c);
 // etc.
};

int main()
{
Complex c(1.0, 1.0); // illegal: no publicly accessible destructor
return 0;
}

--
Pete Becker
Dinkumware, Ltd.
http://www.dinkumware.com
---
[ 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: Mike Ferrel <mferrel@sr.hp.com>
Date: 1998/11/11
Raw View
Buck wrote:
>
> When I declare an interface class, it is always done in one of two forms:
>
> //Form1: Allow pointer destruction
> class CInterface1
> {
>         public:
>         virtual ~CInterface1() {}
>         virtual void func1(int x)  =0;
>         virtual int func2() const =0;
> };
>
> //Form2: Disallow pointer destruction
> class CInterface2
> {
>         public:
>         virtual void func1(int x)  =0;
>         virtual int func2() const =0;
>
>         protected:
>         ~CInterface2() {}
> };
>
> I feel being punished to write more code to prevent
> pointer of interface type from being deleted inadvertently.
>
> Could it be considered into standards whenever an abstrace class is declared
> without a dtor, the dtor is implicitly protected ?
>

But interface types always need virtual destructors, because their
purpose
is to serve as a type for pointers and references to derived objects.
And
those destructors need to be public, so the derived objects can be
destructed
through those base class pointers and references.

Polymorphism can't work otherwise.

Mike Ferrel
---
[ 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: peihao@ms3.hinet.net (Buck)
Date: 1998/11/11
Raw View
On 11 Nov 98 03:06:11 GMT, Mike Ferrel <mferrel@sr.hp.com> wrote:

>But interface types always need virtual destructors, because their
>purpose
>is to serve as a type for pointers and references to derived objects.
>And
>those destructors need to be public, so the derived objects can be
>destructed
>through those base class pointers and references.
>
>Polymorphism can't work otherwise.

Polymorphizm can be used statically, i.e. global or automatic instances.
If the purpose of a specific interface is only to decouple two related components,
and there is no need to do dynamic allocation in practice, why not restrict
the interface from being deleted by its pointer type to earn some efficiency ?
After all you can always feel free to make your dtor virtual, explicitly, and AFAIK
it is the only way to do it.
---
[ 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: abrahams@spam.motu.com (David Abrahams)
Date: 1998/11/11
Raw View
On 10 Nov 98 19:11:56 GMT, Pete Becker <petebecker@acm.org> wrote:

>Buck wrote:
>>
>>
>> Could it be considered into standards whenever an abstrace class is declared
>> without a dtor, the dtor is implicitly protected ?
>
>Probably not. For example:
>
>class Complex
<snip>

Pete, is your Complex class abstract? It doesn't seem to be, and I
think Buck was asking about abstract classes only.

Buck's suggestion would be problematic for other reasons

struct A { virtual void f() = 0; virtual ~A() {} }; // abstract
struct B : A {}; // also abstract

What's wrong with deleting through a B*? It's perfectly appropriate, I
think. Maybe you want a rule that prohibits deleting an abstract base
class that doesn't have a virtual destructor. That seems like a good
candidate for a compiler warning, at least! Buck, I suggest you lobby
your compiler vendor for that warning; you're likely to get it long
before the standard can be changed.

Here's what I'd like from abstract classes: that their constructors
not be required to declare initializers for virtual bases that don't
have default constructors. Since those constructors are always called
by the most-derived class (which, by definition, is never the abstract
class), I find myself having to invent contrived constructor
parameters to write the useless initializer.

-Dave


[ 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              ]