Topic: delete and multiple inheritance breaks malloc?
Author: kanze@us-es.sel.de (James Kanze)
Date: 07 Apr 1994 13:25:30 GMT Raw View
In article <Cn8pLL.27Lr@hawnews.watson.ibm.com> jjb@watson.ibm.com
(John Barton) writes:
|> In article <2msmel$gs0@news.parc.xerox.com>, ellis@parc.xerox.com (John Ellis) writes:
|> |> The draft standard of June 9, 1993 defines "delete" in a way that
|> |> breaks most malloc/free implementations. Was this intentional, and if
|> |> so, did vendors participate in the discussion and seem aware of the
|> |> implications for implementations?
|> |> Section 5.3.4 says:
|> |> ...the value of the operand of delete must be a pointer to a non-
|> |> array object created by a new-expression without a new-placement
|> |> specification, or a pointer to a sub-object representing a base
|> |> class of such an object.
|> This also means that delete on a pointer returned from e.g. the
|> ANSI C string library is illegal. To be sure free() ought to
|> be called on these, but I betcha lots of code calls delete on them...
As far as I can remember, none of the ISO C string libraries (declared
in string.h) return a pointer to heap memory. So generally speaking,
you can only delete/free them if *you* allocated them. In this case,
use delete if you allocated them with new, free if you allocated them
with malloc (and neither if they aren't on the heap.)
--
James Kanze email: kanze@lts.sel.alcatel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung
Author: sudbrak@waldemar.gmd.de (Thomas Sudbrak)
Date: Thu, 31 Mar 1994 09:07:55 GMT Raw View
In <2msmel$gs0@news.parc.xerox.com> ellis@parc.xerox.com (John Ellis) writes:
>The draft standard of June 9, 1993 defines "delete" in a way that
>breaks most malloc/free implementations. Was this intentional, and if
>so, did vendors participate in the discussion and seem aware of the
>implications for implementations?
>Section 5.3.4 says:
> ...the value of the operand of delete must be a pointer to a non-
> array object created by a new-expression without a new-placement
> specification, or a pointer to a sub-object representing a base
> class of such an object.
>Under this definition (which makes perfect sense semantically), a
>pointer to a multiply inherited sub-object can be passed to "delete".
>In all known implementations, this can result in an interior pointer
>(a pointer into the middle of a new-ed object) being passed to
>"delete".
>For example, the following program is legal:
> class A {int i;};
> class B {int i;};
> class C: public A, public B {int i;};
> void main() {
> for (;;) {
> B* b = new C;
> delete b; }}
>But it crashes under Borland 4.0, GNU's g++ 2.5.8, Sun's CC (cfront
>3.0), and Lucid's lcc 2.5. Most C++ implementations of builtin
>new/delete are based on C malloc/free. But I know of no commercial
>malloc/free that allows interior pointers to be passed to "free".
>There are (at least) two implementation approaches for handling
>interior pointers. First, the allocator can maintain some kind of map
>structure that allows arbitrary interior pointers to be mapped to the
>base of an object. Second, the compiler can add a header to every
>sub-object that allows delete to find the base of the containing
>object.
>Maintaining a map adds non-trivial overhead and complexity to
>allocators. The overhead can be kept relatively low, but it requires
>algorithms much more complex than the typical malloc/free. (For
>example, see the allocator in the Boehm garbage collector.)
>Having the compiler add a header to every sub-object is feasible
>according to the language definition, but as a practical matter this
>wouldn't appeal to most of the C++ community. For example, the RTTI
>extension was limited to classes with virtual functions precisely to
>avoid the overhead of adding an extra header word (the vptr) to
>sub-objects that didn't have virtual functions.
The above example does not crash under GNU's g++ 2.5.8 and seems to work
properly if you add virtual empty destructors in each class. This, of
course, results in an extra header word (the vptr). It is a point of
discussion whether this header should be added automatically (thus pro-
viding some level of security) or 'generated' by the programmer by means
of virtual empty destructors.
-------------------------------------------------------------------------------
Thomas Sudbrak (Thomas.Sudbrak@gmd.de) phone: +49 2241 14-2493
System Design Technology Institute fax: +49 2241 14-2384
German National Research Center for Computer Science (GMD)
Schloss Birlinghoven, D-53754 Sankt Augustin, Germany
Author: lep@cfmu.eurocontrol.be (Anh Le-Phuoc)
Date: Fri, 25 Mar 1994 11:23:44 GMT Raw View
In article <2msmel$gs0@news.parc.xerox.com> ellis@parc.xerox.com (John Ellis) writes:
> .... various explainations deleted ...
>
> class A {int i;};
> class B {int i;};
> class C: public A, public B {int i;};
>
> void main() {
> for (;;) {
> B* b = new C;
> delete b; }}
>
>But it crashes under Borland 4.0, GNU's g++ 2.5.8, Sun's CC (cfront
>3.0), and Lucid's lcc 2.5. Most C++ implementations of builtin
>new/delete are based on C malloc/free. But I know of no commercial
>malloc/free that allows interior pointers to be passed to "free".
>
> ... more stuff deleted ...
There is nothing wrong with C++ or (de)allocation mechanism.
The above program will of course crash using ANY compiler. In each
iteration of the loop, you have:
- allocated a C which is 3 * sizeof(int)
- but freed a B which is 1 * sizeof(int)
resulting a memory leak of 2 * sizeof(int).
The operator delete(void * ptr, size_t length) is told be your program
to deallocate sizeof(B).
To deallocate a 'C' properly though a 'B*' you need to define a virtual
destructor for both classes B and C. In general, this is strongly
recommended when using pointers to polymorphic objects.
Try:
class A {int i;};
class B {int i; public: virtual ~B(){} };
class C: public A, public B {int i; public: virtual ~C(){} };
void main() {
for (;;) {
B* b = new C;
delete b; } }
Everything will be OK then.
Hope this will be of help.
--
Anh Le-Phuoc Eurocontrol - Central Flow Management Unit
lep@cfmu.eurocontrol.be 96 Rue de la Fusee, 1130 Bruxelles, Belgium
Tel: +32 2 729 9658
Author: ellis@parc.xerox.com (John Ellis)
Date: 24 Mar 1994 18:33:57 GMT Raw View
The draft standard of June 9, 1993 defines "delete" in a way that
breaks most malloc/free implementations. Was this intentional, and if
so, did vendors participate in the discussion and seem aware of the
implications for implementations?
Section 5.3.4 says:
...the value of the operand of delete must be a pointer to a non-
array object created by a new-expression without a new-placement
specification, or a pointer to a sub-object representing a base
class of such an object.
Under this definition (which makes perfect sense semantically), a
pointer to a multiply inherited sub-object can be passed to "delete".
In all known implementations, this can result in an interior pointer
(a pointer into the middle of a new-ed object) being passed to
"delete".
For example, the following program is legal:
class A {int i;};
class B {int i;};
class C: public A, public B {int i;};
void main() {
for (;;) {
B* b = new C;
delete b; }}
But it crashes under Borland 4.0, GNU's g++ 2.5.8, Sun's CC (cfront
3.0), and Lucid's lcc 2.5. Most C++ implementations of builtin
new/delete are based on C malloc/free. But I know of no commercial
malloc/free that allows interior pointers to be passed to "free".
There are (at least) two implementation approaches for handling
interior pointers. First, the allocator can maintain some kind of map
structure that allows arbitrary interior pointers to be mapped to the
base of an object. Second, the compiler can add a header to every
sub-object that allows delete to find the base of the containing
object.
Maintaining a map adds non-trivial overhead and complexity to
allocators. The overhead can be kept relatively low, but it requires
algorithms much more complex than the typical malloc/free. (For
example, see the allocator in the Boehm garbage collector.)
Having the compiler add a header to every sub-object is feasible
according to the language definition, but as a practical matter this
wouldn't appeal to most of the C++ community. For example, the RTTI
extension was limited to classes with virtual functions precisely to
avoid the overhead of adding an extra header word (the vptr) to
sub-objects that didn't have virtual functions.