🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.std.c++
Article #3554

Re: operators new[]/delete[]

#3554
From: Dietmar Kuehl
Date: Thu, 29 Feb 1996 17:14
145 lines
7379 bytes
Hi,

Pablo Halpern (phalpern@truffle.ultranet.com) wrote:
:   template <class T>
:   T* dup_array(const T* p, size_t s)
:   {
:     T *p2 = reinterpret_cast<T*> new char[s * sizeof(T)];  // note 1

This is in no ways portable. However, it IS portable to allocate "raw"
memory with 'operator new()' or 'operator new[]()'. See below...
:   }

:   void f(T* p, size_t s)
:   {
:     T* newp = dup_array(p, s);
:     // do something with newp
:     delete [] newp;     // note 2

This results indeed in undefined behavior.
:   }

: I believe that something similar to the line marked "note 1" is common
: practice for this sort of operation. However, I believe that the line
: marked "note 2" is undefined behavior. Is there a way in the standard
: can be modified so that the above code becomes well-defined and works as
: intended? How does the STL deal with this in its "unitialized copy"
: operation?

There is no need to modify the standard because there is already a
method available to deal with "raw" memory (see below), which e.g. use
by STL.

: There is another way to allocate raw memory, but here the operations are
: even less defined (I believe):

The operations are well defined, if used correctly...

:    void *p2 = operator new[] (s * sizeof(T));
:    delete p2;  // What does this do? p2 was not the result of a normal
:                // new expression.
:    delete reinterpret_cast<T*> p2;  // What does this do?

Both attempts to release the memory pointed to by 'p2' result in
undefined behavior: 'delete' can only be applied to objects allocated
with 'new T' (for some type 'T').  Likewise, 'delete[]' can only release
array objects allocated with 'new T[i]' (for some type 'T' and some
value 'i'). The whole trick is to distinguish 'new T' from 'operator
new()' and 'delete ptr' from 'operator delete()' (and correspondingly
the array variants):  They are just different operations (see e.g.
"More Effective C++", S.Meyers, Addison-Wesly, Item 8).

I will describe the stuff for arrays because apparently the "renew"
topic is currently "in" :-) 'new T[i]' does something like:

  #include <new>

  T *new_T_array(size_t size) // a "homegrown" 'new T[size]'
  {
    // Allocate enough memory to hold the requested array plus
    // additional information about the size of the array. This
    // is as written NOT portable (insufficient alignment) but it
    // is also not necessary to do the non-portable stuff, if the
    // operations are encapsulated in an array class like 'vector':
    // the size can be stored somewhere else.

    void   *ptr  = operator new[](sizeof(T) + sizeof(size_t));
    size_t *sptr = static_cast<size_t*>(ptr);
    *sptr = size; // first store the size
    // .. then get the address of the actual array
    T *Tptr = static_cast<T*>(static_cast<void*>(sptr + 1));

    // now initialize the array
    for (size_t idx = 0; i < size; ++i)
      operator new(Tptr + idx) T(); 
    return Tptr;
  }

However, this is NOT how it is indeed implemented but it depicts what
is basically going on and how it COULD be implemented (well, not
really: It is also necessary to take care of exceptions in the
constructors and to release constructed objects if there is an
exception). In particular, it shows the basics how to implement an own
routine to allocate an array which basically feels like a built-in
array (i.e. how 'operator new[]()' and "placement new" are used to
create and initialize the array). Unfortunately, you cannot 'delete[]'
an array created with 'new_T_array()'. Instead, you have to mimic the
behavior of 'delete[]' using explicit destruction and 'operator
delete[]()'.  Here are the details:

  void delete_T_array(T *Tptr)
  {
    // Again this code is somewhat non-portable but againn this doesn't
    // matter because an array class can do a better (and portable) job
    // by storing the size somewhere else.

    // First retrieve the size from where it was store in 'new_T_array()':
    size_t *sptr = static_cast<size_t*>(static_cast<void*>(Tptr)) - 1;
    // ... then release the individual objects in the reverse order
    // constructed:
    for (size_t idx = *sptr; idx-- > 0; )
      Tptr[idx].~T();
    
    // Finally release the allocated "raw" memory
    operator delete[](static_cast<void*>(sptr));
  }

Again, this is basically how it works (but e.g. with exception handling
excluded). If you need to do similar stuff, you can do es like this.
However, you should place such stuff in a class (e.g. because it is
much simpler to do this portable). If do so, you are likely to end up
with a class similar to 'vector'.  So, why bother...?

Concerning the 'renew' problematic: Using a combination of the stuff in
'new_T_array()' and 'delete_T_array()' you can implement a
'renew_T_array()' which is capable to 'renew' arrays allocated with
'new_T_array()' (or, even easier, if used with in a class to 'renew'
the internal storage used to represent the array).  However, there is
still a minor inefficiency in comparison with 'realloc()': The need to
store the size. Since the memory management almost certainly "knows"
the size of the memory object somehow (I guess in all implementation it
does know the size but I can also imagine that there could be a strange
environment where this is not the case...), the number of elements in
the memory object could potentially be deduced from this size. There is
no portable method to do so. But I believe that this additional
'size_t' is not that heavy-weight to be a real problem which justifies
some very specific language extension.

: How do we solve these problems in a standard-conforming way. Does there
: need to be special language for char arrays or void * allocations. (Or
: is there already? I haven't found it.)

How to solve this problems in a standard-conforming way: See above.
...  and no, neither are there special allocations for 'char' arrays or
'void*', nor are they needed.
--
dietmar.kuehl@uni-konstanz.de
http://www.informatik.uni-konstanz.de/~kuehl
I am a realistic optimist - that's why I appear to be slightly pessimistic
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your 
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu 
]
Technical Details
Message-ID: <4h4agc$b1m@news.BelWue.DE>
Path: arc1-ca.pugleaf.net!chronicle.mti.sgi.com!austern
References: <313166CF.11C2@orbotech.co.il> <4gu414$62u@mulga.cs.mu.OZ.AU> <4h24c2$pq@caesar.ultra.net>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!chronicle.mti.sgi.com!austern
From: kuehl@uzwil.informatik.uni-konstanz.de (Dietmar Kuehl)
Newsgroups: comp.std.c++
Subject: Re: operators new[]/delete[]
Date: 29 Feb 1996 09:14:42 PST
Organization: Fakultät für Mathematik und Informatik
Approved: austern@isolde.mti.sgi.com
Message-ID: <4h4agc$b1m@news.BelWue.DE>
References: <313166CF.11C2@orbotech.co.il> <4gu414$62u@mulga.cs.mu.OZ.AU> <4h24c2$pq@caesar.ultra.net>
Reply-To: dietmar.kuehl@uni-konstanz.de
NNTP-Posting-Host: isolde.mti.sgi.com
X-Original-Date: 29 Feb 1996 13:42:36 GMT
X-Newsreader: TIN [version 1.2 PL2]
X-Auth: PGPMoose V1.1 PGP comp.std.c++
 iQBVAwUBMTXfEEy4NqrwXLNJAQHRHAH/as7SoRd/95XaCs0i8eQNAYtXQd/KMr6p
 1iSNVwpAkkJpP92slwmxiHc/KUh6qcyXI+C5ZiYvbS/3tulgEKEXig==
 =vxQ2
Originator: austern@isolde.mti.sgi.com