[comp.lang.c++] ``delete[]p'' -- where is the actual size of *p stored?

cline@cheetah.ece.clarkson.edu (Marshall Cline) (08/22/90)

`delete p' (where `p' is a pointer to a base class with a *virtual* dtor)
``does the right thing'' (calls the right dtor) based on the actual type of
the object pointed to by `p'.

But what if Derived is bigger than Base, and `p' points to a bunch of
Derived's (ie: p = new Derived[100]).  Will delete[] (or delete[100]) find
the actual size of the pointed-to objects from the vtable?  Or will it get
this size from something hidden near *p itself?  This information appears
to be necessary in order to avoid messing ones pants...

	class B {
	  //...
	public:
	  //...
	  virtual ~B();
	};

	class D : public B {
	  int D_is_bigger_than_B;
	public:
	  //...
	  ~D();
	};

	main()
	{
	  B* p = new D[100];
	  //...
	  delete [] p;
	}
--
==============================================================================
Marshall Cline / Asst.Prof / ECE Dept / Clarkson Univ / Potsdam, NY 13676
cline@sun.soe.clarkson.edu / Bitnet:BH0W@CLUTX / uunet!clutx.clarkson.edu!bh0w
Voice: 315-268-3868 / Secretary: 315-268-6511 / FAX: 315-268-7600
Career search in progress; ECE faculty; research oriented; will send vita.
PS: If your company is interested in on-site C++/OOD training, drop me a line!
==============================================================================

jimad@microsoft.UUCP (Jim ADCOCK) (08/28/90)

In article <CLINE.90Aug21134409@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>	main()
>	{
>	  B* p = new D[100];
>	  //...
>	  delete [] p;
>	}

C++ will already "do the wrong thing" with any attempts to address the d's
referred to by p, thus rendering any problems with delete [] p moot.
The right thing is for a compiler to issue a stern warning on the
new array statement assigned to an incompatible pointer.

miron@fornax.UUCP (Miron Cuperman) (08/30/90)

jimad@microsoft.UUCP (Jim ADCOCK) writes:

>In article <CLINE.90Aug21134409@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>>	  B* p = new D[100];
>>	  delete [] p;

>C++ will already "do the wrong thing" with any attempts to address the d's
>referred to by p, thus rendering any problems with delete [] p moot.
>The right thing is for a compiler to issue a stern warning on the
>new array statement assigned to an incompatible pointer.

This is not correct.  A pointer to an object may be cast to a pointer
to a base class of that object.  Therefore the above syntax is correct.
From what I understand, the C++ implementation must save the length of
the array in bytes, and deallocate it accordingly.
-- 
	By me: Miron Cuperman <miron@cs.sfu.ca>

ark@alice.UUCP (Andrew Koenig) (08/30/90)

In article <1180@fornax.UUCP>, miron@fornax.UUCP (Miron Cuperman) writes:
> jimad@microsoft.UUCP (Jim ADCOCK) writes:

> >In article <CLINE.90Aug21134409@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
> >>	  B* p = new D[100];
> >>	  delete [] p;

> >C++ will already "do the wrong thing" with any attempts to address the d's
> >referred to by p, thus rendering any problems with delete [] p moot.

> This is not correct.  A pointer to an object may be cast to a pointer
> to a base class of that object.  Therefore the above syntax is correct.

Indeed.  However, p is being used here as a pointer to the initial
element of an array, and that makes all the difference.

If you allocate an array of objects, you must free it through a
pointer to the same type.  Therefore this is illegal:

	B* p = new D[100];
	delete [] p;

For that matter, this won't work either:

	for (int i = 0; i < 100; i++)
		p[i] = /* something */ ;
-- 
				--Andrew Koenig
				  ark@europa.att.com

pcg@cs.aber.ac.uk (Piercarlo Grandi) (09/01/90)

On 30 Aug 90 04:34:52 GMT, miron@fornax.UUCP (Miron Cuperman) said:

miron> jimad@microsoft.UUCP (Jim ADCOCK) writes:

jimad> In article <CLINE.90Aug21134409@cheetah.ece.clarkson.edu>
jimad> cline@sun.soe.clarkson.edu (Marshall Cline) writes:

cline>	  B* p = new D[100];
cline>	  delete [] p;

jimad> C++ will already "do the wrong thing" with any attempts to
jimad> address the d's referred to by p, thus rendering any problems
jimad> with delete [] p moot.

miron> This is not correct.

Jim Adcock did not say it explicitly, but you are casting a (D[100]) to a
(B *), and so doing you lose two types of information: that we are
addressing entities of type D, and 100 of them, because Marshall Cline's
assignment is really read by the compiler as:

	B *p = &((new D[100])[0]);

i.e. p becomes a pointer to the zero'th element of the array.

This means that it becomes impossible for the compiler to know that when
you write p[i] it must implement it as something like '(B *) ((char *) p
+ i*sizeof (D))'; it will instead transform it to '(B *) ((char *) p +
i*sizeof (B))'.

The two expression will only be equivalent when i == 0, and only because
inheritance is implemented as prefixing.


miron> A pointer to an object may be cast to a pointer to a base class
miron> of that object.  Therefore the above syntax is correct.

The syntax is correct. The compiler will also correctly addres *p;
anything else is undefined, because all you have is a pointer to a
single object. Remember, in C and C++ you are only allowed to do things
like *(p + i) if you *know* that p points to an array of object os the
same type as *p, and in this case this is not true.

miron> From what I understand, the C++ implementation must save the length of
miron> the array in bytes, and deallocate it accordingly.

The problem as discussed above is not about the size.

jimad>  The right thing is for a compiler to issue a stern warning on
jimad> the new array statement assigned to an incompatible pointer.

The right thing would be for the rule that allows a base pointer to a
derived class object to be abolished. It has all the insecurity of being
too clever with pointers to unions. Same goes for object assignment. If
you want to do polymorphism, there are ways to do it within the
language, and they are documentedly hazardous. Polymorphism is
(conceptually) difficult, and trying to pretend otherwise is not very
nice.

Another right thing would be to stop pretending that C/C++ really has
arrays; C/C++'s array are really just a polite way of reserving memory
for a pointer to roam about -- real arrays carry with them a descriptor.

We do have something like a[M][N] in C/C++, but then multi dimensional
arrays cause lots of trouble in C/C++ and maybe should be simply
expunged. This is probably the right course in C++, which has mechanisms
to define within the language proper, descriptor based, arrays.
--
Piercarlo "Peter" Grandi           | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk

jimad@microsoft.UUCP (Jim ADCOCK) (09/05/90)

In article <1180@fornax.UUCP| miron@fornax.UUCP (Miron Cuperman) writes:
|jimad@microsoft.UUCP (Jim ADCOCK) writes:
|
|>In article <CLINE.90Aug21134409@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
|>>	  B* p = new D[100];
|>>	  delete [] p;
|
|>C++ will already "do the wrong thing" with any attempts to address the d's
|>referred to by p, thus rendering any problems with delete [] p moot.
|>The right thing is for a compiler to issue a stern warning on the
|>new array statement assigned to an incompatible pointer.
|
|This is not correct.  A pointer to an object may be cast to a pointer
|to a base class of that object.  Therefore the above syntax is correct.
|From what I understand, the C++ implementation must save the length of
|the array in bytes, and deallocate it accordingly.

You did not read what I said.  I said any attempts to *address* the d's
referred to by p will cause "the wrong thing" to happen.  The ability to
cast to a base class pointer is a far different thing from being able
to address an array of Ds as if they were an array of Bs.  Calling delete [] p
for an array of Ds with p a B*, will typically cause some number of B
destructors to be invoked -- which is presumably not what the programmer
intended.  Compilers will accept this syntax, and generate code other
than what the programmer presumably intended.  I consider this a weakness
in the C++ type system -- which is best plugged by a compiler issuing a 
warning: "Do you really want me to generate this nonsensible code?"

peb@Autodesk.COM (Paul Baclaski) (09/20/90)

In article <1180@fornax.UUCP>, miron@fornax.UUCP (Miron Cuperman) writes:
> jimad@microsoft.UUCP (Jim ADCOCK) writes:
> 
> >In article <CLINE.90Aug21134409@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
> >>	  B* p = new D[100];
> >>	  delete [] p;
> ...  Therefore the above syntax is correct.
> From what I understand, the C++ implementation must save the length of
> the array in bytes, and deallocate it accordingly.

This is not true of V1.2 of cfront, but is true for 2.1.  I am not 
sure about 2.0.  C++ *is* converging on the "right way", but it 
does take time.


Paul E. Baclaski
peb@autodesk.com