Topic: We should allow official suppressing of automatically defined


Author: falk.tannhauser@crf.canon.fr (=?ISO-8859-1?Q?Falk_Tannh=E4user?=)
Date: Wed, 18 Aug 2004 05:34:57 GMT
Raw View
John Nagle wrote:

> Lo=EFc Joly wrote:
>=20
>> John Nagle wrote:
>>
>>>    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.
>>>
>>>    That's probably a liveable restriction.  The two most common
>>> reasons for wanting to avoid a virtual destructor are
>>>
>>>    1.    The class is a POD (Plain Old Data), and you don't want
>>>     the vtable overhead.
>>>
>>>    2.    The base class has a non-virtual destructor and wasn't
>>>     designed to be subclassed, but you want to subclass it anyway.
>>>
>>> Case 1 is safe.
>>>
>>> Case 2 is only safe if the derived class doesn't have a destructor.
>>
>>
>>
>> Why so ? Even if the derived class does not have a destructor,=20
>> deleting a derived class through a pointer to the base class is still=20
>> undefined behaviour if the base class does not have a virtual destruct=
or.
>=20
>=20
>    Sorry, by "doesn't have a destructor" I mean "doesn't have a
> non-empty destructor".  All classes have an implicit destructor, of
> course.
>=20
>    It should be defined behavior to delete a class through the
> base pointer provided that no derived class has a non-empty
> destructor.  If that's allowed, and the case with a non-empty
> destructor is disallowed at compile time, we get safety with
> the ability to do the overriding that some people insist is
> necessary.

Not calling the derived class destructor is only one possible
undesirable outcome when a virtual base class constructor is missing.
The following program crashes with "Signal 11" on a recent gcc compiler:
______________________________
struct Base
{
  int i;
  // no virtual function
};

struct Derived : public Base
{
  virtual void foo() {}
};

int main()
{
  Base* b =3D new Derived;
  delete b;
  return 0;
}
______________________________

The reason for this crash is that the 'Base' subobject within the
'Derived' object doesn't begin at the same address than the latter
(since the compiler I used puts the vtable pointer at the beginning
of the 'Derived' object) - something that will also happen in case
of virtual or multiple inheritence. Consequently, a wrong address
is passed to the deallocation function.
As Lo=EFc Joly pointed out, fixing this within the language would
probably imply too much runtime overhead.

Falk

---
[ 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: falk.tannhauser@crf.canon.fr (=?ISO-8859-1?Q?Falk_Tannh=E4user?=)
Date: Wed, 18 Aug 2004 16:54:10 GMT
Raw View
Bob Bell wrote:

> falk.tannhauser@crf.canon.fr (Falk Tannh=E4user) wrote in message news:=
<cfsv0g$s67$1@s5.feed.news.oleane.net>...
>=20
> 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. (It was claimed in this thread that it was not possible
at all).
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.

Falk

---
[ 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: nagle@animats.com (John Nagle)
Date: Sat, 14 Aug 2004 19:14:05 GMT
Raw View
Hyman Rosen wrote:

> John Nagle wrote:
>
>>     If you delete a pointer to a Base, "~Derived" will
>> not be called. That's not safe.
>
>
> You cannot do this, because the Base destructor is protected.

    You can still call the base destructor, even though it is
protected.  You have to do it from inside a function member
of the class, but you can do it.

    It's such subtle incorrect behaviors which should be
prohibited.

    John Nagle

---
[ 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: nagle@animats.com (John Nagle)
Date: Sun, 15 Aug 2004 05:23:02 GMT
Raw View
Hyman Rosen wrote:

> John Nagle wrote:
>
>>     If you delete a pointer to a Base, "~Derived" will
>> not be called. That's not safe.
>
>
> You cannot do this, because the Base destructor is protected.

    You can still call a protected base destructor.  You have
to do it from within a member function of the class, but
you can do it.

    The rules here are complex.  It's better to have the
compiler enforce them than to expect programmers to get
them right.  Especially because getting this right
involves comparing multiple class declarations,
some of which may be controlled by different people.
Compilers are good at that.  People are terrible at it.

    John Nagle
    Animats

---
[ 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: hyrosen@mail.com (Hyman Rosen)
Date: Sun, 15 Aug 2004 06:02:34 GMT
Raw View
John Nagle wrote:
>    You can still call the base destructor, even though it is
> protected.  You have to do it from inside a function member
> of the class, but you can do it.

No, you can't.

>    It's such subtle incorrect behaviors which should be
> prohibited.

They are.

If you think otherwise, please demonstrate with some legal code
that exhibits the problem. For example, the following does *not*:
     class B { protected: ~B() { } };
     class D : public B { void f(B *p) { delete p; } };

---
[ 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: falk.tannhauser@crf.canon.fr (=?ISO-8859-1?Q?Falk_Tannh=E4user?=)
Date: Tue, 17 Aug 2004 16:22:51 GMT
Raw View
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;
}



Falk

---
[ 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: nagle@animats.com (John Nagle)
Date: Tue, 10 Aug 2004 20:23:53 GMT
Raw View
Lo=EFc Joly wrote:
> John Nagle wrote:
>=20
>> Lo=EFc Joly wrote:
>>
>>> John Nagle wrote:

>>    It should be defined behavior to delete a class through the
>> base pointer provided that no derived class has a non-empty
>> destructor.  If that's allowed, and the case with a non-empty
>> destructor is disallowed at compile time, we get safety with
>> the ability to do the overriding that some people insist is
>> necessary.
>=20
> I guess it would be quite difficult to achieve, unless you add some RTT=
I=20
> to all classes, which would be detrimental to performances.
>=20
> For instance :
>=20
> struct A
> {
>   int a;
> };
>=20
> struct B
> {
>   int b;
> };
>=20
> struct C : A, B
> {
> };
>=20
> int f() // should be "void" JN
> {
>   B* foo =3D new C;
>   delete foo;
> }
>=20
> In this case, all classes fit to your proposal, but I guess (I'm not a=20
> complier implementer, so I can only guess) that ensuring f to work is=20
> not an easy task.

    That's a different problem.  C++ doesn't distinguish between
pointers with and without "delete privilege".  Above, "foo"
is not deletable.   But this is unrelated to the destructor
issue.  One can create a non-deletable pointer much more simply,
with

 int x;
 int* p =3D &x;
 delete p;

   I explored this more thoroughly in my "strict C++" proposal.

> If I make an exageration, it looks=20
> to me like if someone told me : "int are difficult to use correclty,=20
> since they can overflow, and almost nobody checks it before any=20
> operation. So let's remove int from the language, and add an infinite=20
> size, dynamically allocated interger class".

    In the VAX era, I once modified the Berkeley C compiler to enable
the VAX integer overflow checking hardware.  That turned up quite a
few bugs in the standard software delivered with 4.1BSD.

    And today, Java does make that check.  Forward progress is being
made on safety, although not by C++.

    John Nagle

---
[ 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: nagle@animats.com (John Nagle)
Date: Fri, 13 Aug 2004 19:10:40 GMT
Raw View
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.

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

   John Nagle
   Animats

---
[ 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: loic.actarus.joly@wanadoo.fr (=?ISO-8859-1?Q?Lo=EFc_Joly?=)
Date: Sat, 7 Aug 2004 21:06:21 GMT
Raw View
John Nagle wrote:

>    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.
>=20
>    That's probably a liveable restriction.  The two most common
> reasons for wanting to avoid a virtual destructor are
>=20
>    1.    The class is a POD (Plain Old Data), and you don't want
>     the vtable overhead.
>=20
>    2.    The base class has a non-virtual destructor and wasn't
>     designed to be subclassed, but you want to subclass it anyway.
>=20
> Case 1 is safe.
>=20
> Case 2 is only safe if the derived class doesn't have a destructor.

Why so ? Even if the derived class does not have a destructor, deleting=20
a derived class through a pointer to the base class is still undefined=20
behaviour if the base class does not have a virtual destructor.

And even though I would appreciate a compiler that tells me about this=20
case, I do not think this is enough to prevent derivation from a base=20
class with no virtual destructor.

The only safe case is when you do not use the class polymorphically, or=20
when the specific compiler you use defines this undefined behavious (in=20
which case, this code is not portable).

One example of a legitimate use (at least, I have not found any solution=20
that I would find better, the alternatives are just too much code to=20
duplicate) is when you want to have a class that behaves just like a=20
std::string, but write in a log file each time a string is created.

You basically have four solutions :
- use string by composition, and define in myString all string member=20
functions, and implement them by forwarding the call to the embeded=20
std::string.
- derive privately from std::string, provide some converion operation to=20
std::string, and write a using clause for all member functions of=20
std::string.
- Make your own class unrelated to std::string and provide some=20
conversion operations.
- Publically derive from std::string, add the constructor, and you are=20
done. Just tell the users not to use this class polymorphically.

I believe that in this case, the burden of forwarding all the interface=20
of string is very cumbersome. And if this base class you cannot modify=20
is modified by its owner (not very often for std::string, but std:: is=20
not the only library I use that I cannot modify), the last solution will=20
not require a modification of your code to take this change into account.

--=20
Lo=EFc


---
[ 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: loic.actarus.joly@wanadoo.fr (=?ISO-8859-1?Q?Lo=EFc_Joly?=)
Date: Tue, 10 Aug 2004 05:53:06 GMT
Raw View
John Nagle wrote:
> Lo=EFc Joly wrote:
>=20
>> John Nagle wrote:
>>
>>>    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.
>>>
>>>    That's probably a liveable restriction.  The two most common
>>> reasons for wanting to avoid a virtual destructor are
>>>
>>>    1.    The class is a POD (Plain Old Data), and you don't want
>>>     the vtable overhead.
>>>
>>>    2.    The base class has a non-virtual destructor and wasn't
>>>     designed to be subclassed, but you want to subclass it anyway.
>>>
>>> Case 1 is safe.
>>>
>>> Case 2 is only safe if the derived class doesn't have a destructor.
>>
>>
>>
>> Why so ? Even if the derived class does not have a destructor,=20
>> deleting a derived class through a pointer to the base class is still=20
>> undefined behaviour if the base class does not have a virtual destruct=
or.
>=20
>=20
>    Sorry, by "doesn't have a destructor" I mean "doesn't have a
> non-empty destructor".  All classes have an implicit destructor, of
> course.
>=20
>    It should be defined behavior to delete a class through the
> base pointer provided that no derived class has a non-empty
> destructor.  If that's allowed, and the case with a non-empty
> destructor is disallowed at compile time, we get safety with
> the ability to do the overriding that some people insist is
> necessary.


I guess it would be quite difficult to achieve, unless you add some RTTI=20
to all classes, which would be detrimental to performances.

For instance :

struct A
{
   int a;
};

struct B
{
   int b;
};

struct C : A, B
{
};

int f()
{
   B* foo =3D new C;
   delete foo;
}

In this case, all classes fit to your proposal, but I guess (I'm not a=20
complier implementer, so I can only guess) that ensuring f to work is=20
not an easy task.

>> And even though I would appreciate a compiler that tells me about this=
=20
>> case, I do not think this is enough to prevent derivation from a base=20
>> class with no virtual destructor.
>=20
>=20
>    You want a stronger restriction?

Not really, I mostly think that your restriction is not placed in the=20
good place.

The stronger restriction already exists : deleting a derived class=20
through a pointer to a base class is undefined behaviour unless the base=20
class has a virtual destructor. The main problem you have with this=20
restriction seems to be that no diagnostic is required.

IMO, trying to restrict what an user can do just in order to ease the=20
diagnostic is a little bit too harsh. If I make an exageration, it looks=20
to me like if someone told me : "int are difficult to use correclty,=20
since they can overflow, and almost nobody checks it before any=20
operation. So let's remove int from the language, and add an infinite=20
size, dynamically allocated interger class".

I agree with you that I would appreciate a compiler that gives a=20
diagnostic in this case. I think it can be done, but I guess it implies=20
some run-time performance cost (one implementation I have in mind is=20
just to make all classes have some RTTI in debug mode, so that you have=20
enough information to check what ought to be checked), so I do not want=20
the standard to require this diagnostic, unless some implementer tells=20
me there is a cost free technique. I all cases, I think it is more a=20
quality of implementation thing than a language thing.

--=20
Lo=EFc

---
[ 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: nagle@animats.com (John Nagle)
Date: Wed, 4 Aug 2004 18:21:26 GMT
Raw View
===================================== MODERATOR'S COMMENT:
 Submitted three times and got sent to three different moderators?  Got
approved and then the newsserver went into a loop?  Lots of possibilities on
a place like Usenet...


===================================== END OF MODERATOR'S COMMENT
Bob Bell wrote:

> 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).

    How did three copies of the same comment make it through
moderation?

   John Nagle
   Animats

---
[ 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: nagle@animats.com (John Nagle)
Date: Wed, 4 Aug 2004 19:11:18 GMT
Raw View
Lo=EFc Joly wrote:
> John Nagle wrote:
>=20
>>    From a safety perspective, this is desirable:
>=20
>=20
> I am not really sure what you mean by safety here. Do you mean easier t=
o=20
> 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.
>=20
> By the way, what do you mean by 'whose initial value is unsafe if=20
> uninitalized' ? From my comprehension of the standard, only with=20
> 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.


>=20
>>    - Any class with a non-virtual destructor cannot be subclassed.
>=20
>=20
> Not very often, but once or twice, I subclassed a class with no virtual=
=20
> destructor.=20

     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".

     If you're overriding a destructor for a class that has a
destructor that does something, the program does have a bug.
A destructor that does something is being silently skipped.
That should be caught at compile time.

    John Nagle
    Animats

---
[ 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: nagle@animats.com (John Nagle)
Date: Thu, 5 Aug 2004 16:39:14 GMT
Raw View
Hyman Rosen wrote:
> John Nagle wrote:
>
>>     If you're overriding a destructor for a class that has a
>> destructor that does something, the program does have a bug.
>> A destructor that does something is being silently skipped.
>> That should be caught at compile time.
>
>
> No. This is not an error, and will break existing code.
> It is perfectly OK to derive from a class which has a
> non-virtual "does something" destructor, and to have a
> "does something" destructor in the derived class.
>
> The only time an error occurs is if an object of this
> derived class is deleted through a pointer to the base
> class. If the object is deleted through a pointer to
> the derived class, or if an automatic object of the
> derived type goes out of scope, the entire chain of
> destructors is called properly.

     That's easy to check for at run time.  If the size
at "delete" doesn't match the proper size for the type,
an error should be reported.

   John Nagle
   Animats

---
[ 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: brangdon@cix.co.uk (Dave Harris)
Date: Thu, 5 Aug 2004 21:29:13 GMT
Raw View
nagle@animats.com (John Nagle) wrote (abridged):
>      That's easy to check for at run time.  If the size
> at "delete" doesn't match the proper size for the type,
> an error should be reported.

Sometimes an instance of a derived class is the same size as an instance
of its base class.

In any case, it sounds like a quality of implementation issue.
Implementations should not be required to store the size of the object.
(Some only store the size of the memory block which holds it, which may be
rounded up to something bigger.)

-- Dave Harris, Nottingham, UK

---
[ 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: nagle@animats.com (John Nagle)
Date: Sat, 7 Aug 2004 04:55:28 GMT
Raw View
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.

    That's probably a liveable restriction.  The two most common
reasons for wanting to avoid a virtual destructor are

    1. The class is a POD (Plain Old Data), and you don't want
 the vtable overhead.

    2. The base class has a non-virtual destructor and wasn't
 designed to be subclassed, but you want to subclass it anyway.

Case 1 is safe.

Case 2 is only safe if the derived class doesn't have a destructor.

I would argue that the unsafe cases should be prohibited.  The safe
cases seem to cover all the things you really need to do.

GCC issues warnings for missing virtual destructors, so we have some
experience with enforcement of rules in this area.  It doesn't seem
to be an onerous burden.

    John Nagle
    Animats

---
[ 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: nagle@animats.com (John Nagle)
Date: Mon, 2 Aug 2004 19:26:30 GMT
Raw View
Francis Glassborow wrote:

> 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.

    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.

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

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.

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.

     John Nagle
     Animats

---
[ 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: loic.actarus.joly@wanadoo.fr (=?ISO-8859-1?Q?Lo=EFc_Joly?=)
Date: Tue, 3 Aug 2004 01:30:00 GMT
Raw View
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 to=20
use correctly, or more resilent to piracy ?


>    - 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.

At one time, I was a proponent of 'all variables should be initialised,=20
if we do not know to which value, default initialise them'. I was told=20
that this wuold be very detrimental for performances is some cases. I=20
beleived your proposal would have the same effect.

By the way, what do you mean by 'whose initial value is unsafe if=20
uninitalized' ? From my comprehension of the standard, only with=20
unsigned char are we sure that any uninitialised value is safe.

>    - Any class with a non-virtual destructor cannot be subclassed.

Not very often, but once of twice, I subclassed a class with no virtual=20
destructor. And I beleive there was no better solution (this usually=20
happens when I cannot modify the base class or when performaces really=20
matter, and I know this type will not be used polymorphically, it was=20
derivation for re-use, but delegation was not a very good solution,=20
since the base class interface was very large).

>    - If you need a copy constructor, you also need a destructor
>      and "operator=3D".  Enforce this.
>=20
> With this enforcement, most of the mistakes one can make with
> the defaults become errors.
>=20
> 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.
>=20
> I realize there's a "I am so l33t I should be allowed to break
> the rules when I want to" lobby.

I'm rather part of the "sometimes, I have no choice" lobby. I beleive=20
all your propositions can be implemented with no change in the standard,=20
as compiler warnings. They are pretty good candidates for such=20
warnings., and maybe as a non normative note, the standard should=20
encourage those verifications, but if they are mandatory, and there is=20
no possibility for the user to force the previous behaviour, it would=20
hurt the programmers in some rarely encountered but nevertheless=20
existant and annoying cases.

>  They should be overridden.
> Safety is more important.  C++ is the least safe language in
> mainstream use today.=20

What about C ? At least, in C++, there is only a very limited number of=20
points you have to be careful of in order to make a class safe. Compare=20
std::string to char* and its infamous buffer overflows.

> This needs to be recognized as a serious
> problem.

--=20
Lo=EFc

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