Topic: We should allow official suppressing of automatically defined (and "pure" virtual) class members


Author: belvis@pacbell.net (Bob Bell)
Date: Thu, 19 Aug 2004 22:34:07 GMT
Raw View
falk.tannhauser@crf.canon.fr (Falk Tannh   user) wrote in message news:<cfvpk0$d78$1@s5.feed.news.oleane.net>...
> Bob Bell wrote:
>
> > falk.tannhauser@crf.canon.fr (Falk Tannh user) wrote in message news:
>  <cfsv0g$s67$1@s5.feed.news.oleane.net>...
> >
> > I'm not sure I understand your point. I was trying to show legitimate
> > ways to derive from a base class without needing a virtual destructor.
> > What's the point of rewriting the examples to include undefined
> > behavior?
>
> The point was to show that, even when making the base class destructor
> protected, or even when deriving privately, it is still possible
> (although more difficult) to delete a derived class object through
> a base class pointer, and thus trigger UB when the base class destructor
> isn't virtual.

I can take any piece of code and modify it to add undefined behavior.
The fact that I can do that doesn't demonstrate a problem with the
original code.

> (It was claimed in this thread that it was not possible
> at all).

In the examples I gave, it wasn't possible.

> However, I acknowledge that there are legitimate cases when one may
> derive from a base class not having a virtual destructor (traits classes
> such as std::iterator, std::unary function and std::binary function come
> to mind). Objects of classes derived from these ones are not usually
> dynamically allocated (not to mention polymorphically deleted), therefore
> the problem we are discussing doesn't practically occur. (To make such
> classes foolproof, one could declare 'operator delete' private in the
> base class and not implement it).
> But it seems difficult to me to introduce stricter rules into the
> language without disallowing legitimate constructs.

I agree completely.

Bob

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Sat, 14 Aug 2004 21:16:18 GMT
Raw View
nagle@animats.com (John Nagle) wrote in message news:<of6Tc.6188$aM4.2962@newssvr29.news.prodigy.com>...
> Bob Bell wrote:
>
> > nagle@animats.com (John Nagle) wrote in message news:<ZRYQc.1031$QJ3.966@newssvr21.news.prodigy.com>...
> >
> >>Bob Bell wrote:
>
> >
> > class Base {
> >    public:
> >       Base();
> >    protected:
> >       ~Base();
> > };
> >
> > class Derived : public Base {
> >    public:
> >       ~Derived() { /* non-empty code */ }
> > };
> >
> > This is perfectly safe, yet ruled out by your ideas.
>
>      If you delete a pointer to a Base, "~Derived" will
> not be called. That's not safe.

It's not possible to delete a pointer to a Derived object through a
pointer to a Base; the Base destructor is protected.

>      One can construct convoluted examples of marginal
> code.  In serious programming, such code is not desirable.

This isn't convoluted. It's a perfectly safe, perfectly legitimate way
to derive from a class without a virtual destructor.

Bob

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Wed, 18 Aug 2004 02:08:34 GMT
Raw View
falk.tannhauser@crf.canon.fr (Falk Tannh   user) wrote in message news:<cfsv0g$s67$1@s5.feed.news.oleane.net>...
> Bob Bell wrote:
> >
> > class Base {
> >    public:
> >       Base();
> >    protected:
> >       ~Base();
> > };
> >
> > class Derived : public Base {
> >    public:
> >       ~Derived() { /* non-empty code */ }
> > };
> >
> > This is perfectly safe, yet ruled out by your ideas.
>
> class Base
> {
> public:
>   Base() {}
>   static void kill(Base* b) { delete b; }
>
> protected:
>   ~Base() {}
> }; // class Base
>
> class Derived : public Base
> {
> public:
>   ~Derived() {/* non-empty code here */ }
> }; // class Derived
>
> int main()
> {
>   Base::kill(new Derived); // Ouch!
>   return 0;
> }
>
>
> > Here's another safe-yet-ruled-out derivation:
> >
> > class Base {
> > };
> >
> > class Derived : private Base {
> >    public:
> >       ~Derived() { /* non-empty code here */ }
> > };
>
> class Base
> {
> protected:
>   static void kill(Base* b) { delete b; }
> }; // class Base
>
> class Derived : private Base
> {
> public:
>   static void kill(Derived* d) { Base::kill(d); }
>   ~Derived() { /* non-empty code here */ }
> };
>
> int main()
> {
>   Derived::kill(new Derived); // Ouch again!
>   return 0;
> }

I'm not sure I understand your point. I was trying to show legitimate
ways to derive from a base class without needing a virtual destructor.
What's the point of rewriting the examples to include undefined
behavior?

Bob

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Thu, 12 Aug 2004 21:17:04 GMT
Raw View
nagle@animats.com (John Nagle) wrote in message news:<ZRYQc.1031$QJ3.966@newssvr21.news.prodigy.com>...
> Bob Bell wrote:
> > nagle@animats.com (John Nagle) wrote in message news:<ib9Qc.477$nz1.48@newssvr27.news.prodigy.com>...
> >>     Define an "empty destructor" as a default destructor for a class
> >>which contains no data members with non-empty destructors.  An
> >>"empty destructor" doesn't do anything to the object before
> >>its space is released.
> >>
> >>     Then, we should have the rule that a class with a non-empty
> >>non-virtual destructor cannot be subclassed.
> >>
> >>     This is both safe and tolerant of existing valid code.
> >>It's OK to have a non-virtual destructor in a derived class
> >>when it's harmless.  This defines and checks "harmless".
> >
> >
> > Let's see if I can post only once this time. ;-)
> >
> > Except that when you have a base class with a non-virtual empty
> > destructor, and a derived class with a non-empty destructor, and an
> > object of the derived class is destroyed through a pointer to the base
> > class, you still have undefined behavior. So you end up disallowing
> > some legitimate uses of inheritance while not actually preventing the
> > problems from occurring.
>
>     True.
>
>     So correctness requires that, if you have a non-virtual destructor
> in a base class, you can't have a non-empty destructor in a class
> derived from it.

class Base {
   public:
      Base();
   protected:
      ~Base();
};

class Derived : public Base {
   public:
      ~Derived() { /* non-empty code */ }
};

This is perfectly safe, yet ruled out by your ideas.

Here's another safe-yet-ruled-out derivation:

class Base {
};

class Derived : private Base {
   public:
      ~Derived() { /* non-empty code here */ }
};

Bob

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Tue, 3 Aug 2004 19:15:24 GMT
Raw View
nagle@animats.com (John Nagle) wrote in message news:<ZjwPc.102953$6r3.60036@newssvr29.news.prodigy.com>...
>     From a safety perspective, this is desirable:
>
>     - If a class has a private or protected member whose initial
>       value is unsafe if uninitalized (typically a pointer), it
>       must have an explicit constructor which initializes that
>       value.  Otherwise, the object is "born broken", with
>       a random value in a pointer.
>
>     - Any class with a non-virtual destructor cannot be subclassed.
>
>     - If you need a copy constructor, you also need a destructor
>       and "operator=".  Enforce this.

Absolutely not. IMHO, none of these are desireable. I don't believe
it's the job of the language to hold my hand in this way. If you want
this kind of thing, there are other languages that do this much better
than C++ (e.g., Java).

> With this enforcement, most of the mistakes one can make with
> the defaults become errors.

As do some appropriate uses of the defaults.

> This requires no new syntax, but causes some programs currently
> considered valid to be in error.  Such programs are probably
> broken, and forcing a cleanup is a positive good.

A practical definition of "broken" would be something along the lines
of "doesn't work." Presumably, the programs you say are probably
broken are currently working.

> I realize there's a "I am so l33t I should be allowed to break
> the rules when I want to" lobby.  They should be overridden.
> Safety is more important.  C++ is the least safe language in
> mainstream use today.  This needs to be recognized as a serious
> problem.

Sorry, can't agree. It has nothing to do with being "l33t", it's just
that C++ is not designed to be "safe" ("safe" in the sense of
preventing you from making mistakes). I think the approach of n1582
makes a lot more sense, because it doesn't attempt to restrict the
kinds of programs I can write, whereas your idea of safety does. Also,
it doesn't break existing code; your idea does.

I have had valid reasons to violate all three of your safety
guidelines; having the language forbid me from doing so would not have
made my programs any safer, but would have made them more complicated.

Bob

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Fri, 6 Aug 2004 18:22:10 GMT
Raw View
nagle@animats.com (John Nagle) wrote in message news:<ib9Qc.477$nz1.48@newssvr27.news.prodigy.com>...
> Lo c Joly wrote:
> > John Nagle wrote:
> >
> >>    From a safety perspective, this is desirable:
> >
> >
> > I am not really sure what you mean by safety here. Do you mean easier t
>  o
> > use correctly, or more resilent to piracy ?
>
>     I mean less likely to result in defective programs.
>
>     A clear understanding of language safety issues is essential
> for language design.
>
> >>    - If a class has a private or protected member whose initial
> >>      value is unsafe if uninitalized (typically a pointer), it
> >>      must have an explicit constructor which initializes that
> >>      value.  Otherwise, the object is "born broken", with
> >>      a random value in a pointer.
> >
> > By the way, what do you mean by 'whose initial value is unsafe if
> > uninitalized' ? From my comprehension of the standard, only with
> > unsigned char are we sure that any uninitialised value is safe.
>
>     Given the way the standard is written, that's a pickey, but
> probably correct, point.  In theory, you could have an implementation
> for a machine for which not all bit patterns are valid integers.
> Decimal machines, like the IBM 1401, the IBM 1620, and the UNIVAC I
> have that property.   Somehow, I don't think that backwards
> compatibility with machines from the early 1960s is a major issue.
> Since C++ allows shift operations on integers, there's an
> assumption that the underlying representation is binary.
>
>
> >
> >>    - Any class with a non-virtual destructor cannot be subclassed.
> >
> >
> > Not very often, but once or twice, I subclassed a class with no virtual
>
> > destructor.
>
>      We can fix that.
>
>      Define an "empty destructor" as a default destructor for a class
> which contains no data members with non-empty destructors.  An
> "empty destructor" doesn't do anything to the object before
> its space is released.
>
>      Then, we should have the rule that a class with a non-empty
> non-virtual destructor cannot be subclassed.
>
>      This is both safe and tolerant of existing valid code.
> It's OK to have a non-virtual destructor in a derived class
> when it's harmless.  This defines and checks "harmless".

Let's see if I can post only once this time. ;-)

Except that when you have a base class with a non-virtual empty
destructor, and a derived class with a non-empty destructor, and an
object of the derived class is destroyed through a pointer to the base
class, you still have undefined behavior. So you end up disallowing
some legitimate uses of inheritance while not actually preventing the
problems from occurring.

Bob

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dwalker07@snet.net (Daryle Walker)
Date: Thu, 29 Jul 2004 15:58:43 GMT
Raw View
I was going to write on this subject completely from my own thoughts,
but I've also looked at:

 <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1582.pdf>

I think the problem is worse than that author stated.  A class
automatically gets:
   1. default constructor
   2. copy constructor
   3. destructor (non-virtual)
   4. operator =
   5. operator & (unary, i.e. address-of)
   6. operator ,

So that proposal also needs to consider [5] and [6].

A big problem with blocking any of the list above is that the current
method is purely convention.  You just declare the method private and
then don't define it.  The flaw is that the compiler _cannot_ check the
"never gets defined" part.  In fact, there could be legitimate uses of
declaring these methods private and defining them anyway.  Since the
compiler can't distinguish the two cases, it can't optimize the common
first case since it can't risk that we actually have the second case.

Finally, we have a similar problem with "pure" virtual methods.  These
don't actually exist in C++ either.  Programmers use the same "declare
but never define" trick.  But "pure" virtual methods can be defined.
(In fact, a derived class can use them in its mandatory re-definition!)

--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Fri, 30 Jul 2004 04:34:41 GMT
Raw View
In article
<dwalker07-0BF192.03190429072004@newsclstr01.news.prodigy.com>, Daryle
Walker <dwalker07@snet.net> writes
>I was going to write on this subject completely from my own thoughts,
>but I've also looked at:
>
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1582.pdf>
>
>I think the problem is worse than that author stated.  A class
>automatically gets:
>   1. default constructor
>   2. copy constructor
>   3. destructor (non-virtual)
>   4. operator =
>   5. operator & (unary, i.e. address-of)
>   6. operator ,
>
>So that proposal also needs to consider [5] and [6].

They are already under consideration. 6 will probably not be touched, 5
is a matter of considering pros and cons. Ideas presented here will be
noted.

>
>A big problem with blocking any of the list above is that the current
>method is purely convention.  You just declare the method private and
>then don't define it.  The flaw is that the compiler _cannot_ check the
>"never gets defined" part.  In fact, there could be legitimate uses of
>declaring these methods private and defining them anyway.  Since the
>compiler can't distinguish the two cases, it can't optimize the common
>first case since it can't risk that we actually have the second case.

Which is another reason for the concept of an explicit class.
>
>Finally, we have a similar problem with "pure" virtual methods.  These
>don't actually exist in C++ either.  Programmers use the same "declare
>but never define" trick.  But "pure" virtual methods can be defined.
>(In fact, a derived class can use them in its mandatory re-definition!)

No, pure virtual methods are an entirely different issue already covered
by the Standard. Pure virtuals must have a subsequent overrider in a
derived class. Providing a definition in the ABC has no effect on that
requirement.


--
Francis Glassborow      ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: NULL@NULL.NULL (JKop)
Date: Mon, 2 Aug 2004 23:04:10 GMT
Raw View
> A class
> automatically gets:
>    1. default constructor
>    2. copy constructor
>    3. destructor (non-virtual)
>    4. operator =
>    5. operator & (unary, i.e. address-of)
>    6. operator ,


Would you not say that there's 8, ie.

class SomeClass
{
public:
     SomeClass();
     SomeClass(const SomeClass&);
     ~SomeClass();
     SomeClass& operator=(const SomeClass&);
     SomeClass* operator&();
     const SomeClass* operator&() const;
     template<class T> T operator,(T&) const;
     template<class T> const T operator,(const T&) const;
};


-JKop

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: tslettebo@hotmail.com ("Terje Sletteb ")
Date: Tue, 3 Aug 2004 01:29:59 GMT
Raw View
"John Nagle" <nagle@animats.com> wrote in message
news:ZjwPc.102953$6r3.60036@newssvr29.news.prodigy.com...
>
>     From a safety perspective, this is desirable:
>
>     - Any class with a non-virtual destructor cannot be subclassed.

I think this is very much debatable! :)

There's been lots of threads on this. Two possibilities - both being safe -
is to have the destructor public and virtual, or protected and non-virtual.
The latter case ensures that the class has to be inherited from, and that
you can't delete an object of it through a pointer to the base class, so
it's safe to have a non-virtual destructor.

Inheriting from a class with a public non-virtual destructor is also very
common. Take for example the standard library's unary_function and
binary_function.

It's also quite a lot in use in template metaprogramming.

>     - If you need a copy constructor, you also need a destructor
>       and "operator=".  Enforce this.

There may also be legetimate cases where this doesn't apply. A compiler
might give a "notice" about it, though, just as it may do so if you have a
non-vritual destructor (and some do give a "notice" about this).

Regards,

Terje


---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: NULL@NULL.NULL (JKop)
Date: Tue, 3 Aug 2004 13:14:31 GMT
Raw View
> class SomeClass
> {
> public:
>          SomeClass();
>          SomeClass(const SomeClass&);
>          ~SomeClass();
>          SomeClass& operator=(const SomeClass&);
>          SomeClass* operator&();
>          const SomeClass* operator&() const;
>          template<class T> T operator,(T&) const;
>          template<class T> const T operator,(const T&) const;
> };

CORRECTION

The comma operator should return a reference.

class SomeClass
{
public:
         SomeClass();
         SomeClass(const SomeClass&);
         ~SomeClass();
         SomeClass& operator=(const SomeClass&);
         SomeClass* operator&();
         const SomeClass* operator&() const;
         template<class T> T& operator,(T&) const;
         template<class T> const T& operator,(const T&) const;
};


-JKop

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Tue, 3 Aug 2004 17:05:48 GMT
Raw View
nagle@animats.com (John Nagle) wrote in message news:<ZjwPc.102953$6r3.60036@newssvr29.news.prodigy.com>...
>     From a safety perspective, this is desirable:
>
>     - If a class has a private or protected member whose initial
>       value is unsafe if uninitalized (typically a pointer), it
>       must have an explicit constructor which initializes that
>       value.  Otherwise, the object is "born broken", with
>       a random value in a pointer.
>
>     - Any class with a non-virtual destructor cannot be subclassed.
>
>     - If you need a copy constructor, you also need a destructor
>       and "operator=".  Enforce this.

Absolutely not. IMHO, none of these are desireable. I don't believe
it's the job of the language to hold my hand in this way. If you want
this kind of thing, there are other languages that do this much better
than C++ (e.g., Java).

> With this enforcement, most of the mistakes one can make with
> the defaults become errors.

As do some appropriate uses of the defaults.

> This requires no new syntax, but causes some programs currently
> considered valid to be in error.  Such programs are probably
> broken, and forcing a cleanup is a positive good.

A practical definition of "broken" would be something along the lines
of "doesn't work." Presumably, the programs you say are probably
broken are currently working.

> I realize there's a "I am so l33t I should be allowed to break
> the rules when I want to" lobby.  They should be overridden.
> Safety is more important.  C++ is the least safe language in
> mainstream use today.  This needs to be recognized as a serious
> problem.

Sorry, can't agree. It has nothing to do with being "l33t", it's just
that C++ is not designed to be "safe" ("safe" in the sense of
preventing you from making mistakes). I think the approach of n1582
makes a lot more sense, because it doesn't attempt to restrict the
kinds of programs I can write, whereas your idea of safety does. Also,
it doesn't break existing code; your idea does.

I have had valid reasons to violate all three of your safety
guidelines; having the language forbid me from doing so would not have
made my programs any safer, but would have made them more complicated.

Bob

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Tue, 3 Aug 2004 19:10:04 GMT
Raw View
nagle@animats.com (John Nagle) wrote in message news:<ZjwPc.102953$6r3.60036@newssvr29.news.prodigy.com>...
>     From a safety perspective, this is desirable:
>
>     - If a class has a private or protected member whose initial
>       value is unsafe if uninitalized (typically a pointer), it
>       must have an explicit constructor which initializes that
>       value.  Otherwise, the object is "born broken", with
>       a random value in a pointer.
>
>     - Any class with a non-virtual destructor cannot be subclassed.
>
>     - If you need a copy constructor, you also need a destructor
>       and "operator=".  Enforce this.

Absolutely not. IMHO, none of these are desireable. I don't believe
it's the job of the language to hold my hand in this way. If you want
this kind of thing, there are other languages that do this much better
than C++ (e.g., Java).

> With this enforcement, most of the mistakes one can make with
> the defaults become errors.

As do some appropriate uses of the defaults.

> This requires no new syntax, but causes some programs currently
> considered valid to be in error.  Such programs are probably
> broken, and forcing a cleanup is a positive good.

A practical definition of "broken" would be something along the lines
of "doesn't work." Presumably, the programs you say are probably
broken are currently working.

> I realize there's a "I am so l33t I should be allowed to break
> the rules when I want to" lobby.  They should be overridden.
> Safety is more important.  C++ is the least safe language in
> mainstream use today.  This needs to be recognized as a serious
> problem.

Sorry, can't agree. It has nothing to do with being "l33t", it's just
that C++ is not designed to be "safe" ("safe" in the sense of
preventing you from making mistakes). I think the approach of n1582
makes a lot more sense, because it doesn't attempt to restrict the
kinds of programs I can write, whereas your idea of safety does. Also,
it doesn't break existing code; your idea does.

I have had valid reasons to violate all three of your safety
guidelines; having the language forbid me from doing so would not have
made my programs any safer, but would have made them more complicated.

Bob

---
[ 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://www.jamesd.demon.co.uk/csc/faq.html                       ]