C++ MEMORY ALLOCATION

PREVIOUS                                                                                                                    NEXT

MEMORY ALLOCATION/ DEALLOCATION

Q: What is the difference between new/delete and malloc/free?
A: Malloc/free do not know about constructors and destructors. New and delete create and
destroy objects, while malloc and free allocate and deallocate memory
.
Q: What is difference between new and malloc?
A: Both malloc and new functions are used for dynamic memory allocations and the basic
difference is: malloc requires a special "typecasting" when it allocates memory for eg. if the
pointer used is the char pointer then after the processor allocates memory then this allocated
memory needs to be typecasted to char pointer i.e (char*).but new does not requires any
typecasting. Also, free is the keyword used to free the memory while using malloc and delete the
keyword to free memory while using new, otherwise this will lead the memory leak.

Q: What is the difference between delete and delete[]?
A: Whenever you allocate memory with new[], you have to free the memory using delete[].
When you allocate memory with 'new', then use 'delete' without the brackets. You use new[] to
allocate an array of values (always starting at the index 0).

Q: What is difference between malloc()/free() and new/delete?
A: malloc allocates memory for object in heap but doesn't invoke object's constructor to initialize
the object. new allocates memory and also invokes constructor to initialize the object. malloc()
and free() do not support object semantics, does not construct and destruct objects
Eg. string * ptr = (string *)(malloc (sizeof(string))) Are not safe, and does not calculate the size
of the objects that it construct
The following return a pointer to void
int *p = (int *) (malloc(sizeof(int)));
int *p = new int;
Are not extensible
new and delete can be overloaded in a class
"delete" first calls the object's termination routine (i.e. its destructor) and then releases the space
the object occupied on the heap memory. If an array of objects was created using new, then
delete must be told that it is dealing with an array by preceding the name with an empty []:-Int_t *my_ints = new Int_t[10];
...
delete []my_ints;

Q: What is the difference between "new" and "operator new" ?A:"operator new" works like malloc.

Q: What is Memory alignment??
A: The term alignment primarily means the tendency of an address pointer value to be a multiple
of some power of two. So a pointer with two byte alignment has a zero in the least signi_cant bit.
And a pointer with four byte alignment has a zero in both the two least signi_cant bits. And so
on. More alignment means a longer sequence of zero bits in the lowest bits of a pointer.

Q: Is there a way to force new to allocate memory from a specific memory area?A: Yes. The good news is that these "memory pools" are useful in a number of situations. The
bad news is that I'll have to drag you through the mire of how it works before we discuss all the
uses. But if you don't know about memory pools, it might be worthwhile to slog through this
FAQ you might learn something useful!
First of all, recall that a memory allocator is simply supposed to return uninitialized bits of
memory; it is not supposed to produce "objects." In particular, the memory allocator is not
supposed to set the virtual-pointer or any other part of the object, as that is the job of the
constructor which runs after the memory allocator. Starting with a simple memory allocator
function, allocate(), you would use placement new to construct an object in that memory. In
other words, the following is morally equivalent to new Foo():
void* raw = allocate(sizeof(Foo)); // line 1
Foo* p = new(raw) Foo(); // line 2
Okay, assuming you've used placement new and have survived the above two lines of code, the
next step is to turn your memory allocator into an object. This kind of object is called a "memory
pool" or a "memory arena." This lets your users have more than one "pool" or "arena" from
which memory will be allocated. Each of these memory pool objects will allocate a big chunk of
memory using some specific system call (e.g., shared memory, persistent memory, stack
memory, etc.; see below), and will dole it out in little chunks as needed. Your memory-pool class
might look something like this:
class Pool {
public:
void* alloc(size_t nbytes);
void dealloc(void* p);
private:
...data members used in your pool object...
};
void* Pool::alloc(size_t nbytes)
{
...your algorithm goes here...
}
void Pool::dealloc(void* p)
{
...your algorithm goes here...
}
Now one of your users might have a Pool called pool, from which they could allocate objects
like this:
Pool pool;
...
void* raw = pool.alloc(sizeof(Foo));
Foo* p = new(raw) Foo();
Or simply:
Foo* p = new(pool.alloc(sizeof(Foo))) Foo();
The reason it's good to turn Pool into a class is because it lets users create N different pools of
memory rather than having one massive pool shared by all users. That allows users to do lots of
funky things. For example, if they have a chunk of the system that allocates memory like crazy
then goes away, they could allocate all their memory from a Pool, then not even bother doing
any deletes on the little pieces: just deallocate the entire pool at once. Or they could set up a
"shared memory" area (where the operating system specifically provides memory that is shared
between multiple processes) and have the pool dole out chunks of shared memory rather than
process-local memory. Another angle: many systems support a non-standard function often
called alloca() which allocates a block of memory from the stack rather than the heap. Naturally
this block of memory automatically goes away when the function returns, eliminating the need
for explicit deletes. Someone could use alloca() to give the Pool its big chunk of memory, then
all the little pieces allocated from that Pool act like they're local: they automatically vanish when
the function returns. Of course the destructors don't get called in some of these cases, and if the
destructors do something nontrivial you won't be able to use these techniques, but in cases where
the destructor merely deallocates memory, these sorts of techniques can be useful.
Okay, assuming you survived the 6 or 8 lines of code needed to wrap your allocate function as a
method of a Pool class, the next step is to change the syntax for allocating objects. The goal is to
change from the rather clunky syntax new(pool.alloc(sizeof(Foo))) Foo() to the simpler syntax
new(pool) Foo(). To make this happen, you need to add the following two lines of code just
below the definition of your Pool class:
inline void* operator new(size_t nbytes, Pool& pool)
{
return pool.alloc(nbytes);
}
Now when the compiler sees new(pool) Foo(), it calls the above operator new and passes
sizeof(Foo) and pool as parameters, and the only function that ends up using the funky
pool.alloc(nbytes) method is your own operator new.
Now to the issue of how to destruct/deallocate the Foo objects. Recall that the brute force
approach sometimes used with placement new is to explicitly call the destructor then explicitly
deallocate the memory:
void sample(Pool& pool)
{
Foo* p = new(pool) Foo();
...
p->~Foo(); // explicitly call dtor
pool.dealloc(p); // explicitly release the memory
}
This has several problems, all of which are fixable:
The memory will leak if Foo::Foo() throws an exception.
The destruction/deallocation syntax is different from what most programmers are used to, so
they'll probably screw it up.
Users must somehow remember which pool goes with which object. Since the code that allocates
is often in a different function from the code that deallocates, programmers will have to pass
around two pointers (a Foo* and a Pool*), which gets ugly fast (example, what if they had an
array of Foos each of which potentially came from a different Pool; ugh).
We will fix them in the above order.
Problem #1: plugging the memory leak. When you use the "normal" new operator, e.g., Foo* p =
new Foo(), the compiler generates some special code to handle the case when the constructor
throws an exception. The actual code generated by the compiler is functionally similar to this:
// This is functionally what happens with Foo* p = new Foo()
Foo* p;
// don't catch exceptions thrown by the allocator itself
void* raw = operator new(sizeof(Foo));
// catch any exceptions thrown by the ctor
try {
p = new(raw) Foo(); // call the ctor with raw as this
}
catch (...) {
// oops, ctor threw an exception
operator delete(raw);
throw; // rethrow the ctor's exception
}
The point is that the compiler deallocates the memory if the ctor throws an exception. But in the
case of the "new with parameter" syntax (commonly called "placement new"), the compiler
won't know what to do if the exception occurs so by default it does nothing:
// This is functionally what happens with Foo* p = new(pool) Foo():
void* raw = operator new(sizeof(Foo), pool);
// the above function simply returns "pool.alloc(sizeof(Foo))"
Foo* p = new(raw) Foo();
// if the above line "throws", pool.dealloc(raw) is NOT called
So the goal is to force the compiler to do something similar to what it does with the global new
operator. Fortunately it's simple: when the compiler sees new(pool) Foo(), it looks for a
corresponding operator delete. If it finds one, it does the equivalent of wrapping the ctor call in a
try block as shown above. So we would simply provide an operator delete with the following
signature (be careful to get this right; if the second parameter has a different type from the
second parameter of the operator new(size_t, Pool&), the compiler doesn't complain; it simply
bypasses the try block when your users say new(pool) Foo()):
void operator delete(void* p, Pool& pool)
{
pool.dealloc(p);
}
After this, the compiler will automatically wrap the ctor calls of your new expressions in a try
block:
// This is functionally what happens with Foo* p = new(pool) Foo()
Foo* p;
// don't catch exceptions thrown by the allocator itself
void* raw = operator new(sizeof(Foo), pool);
// the above simply returns "pool.alloc(sizeof(Foo))"
// catch any exceptions thrown by the ctor
try {
p = new(raw) Foo(); // call the ctor with raw as this
}
catch (...) {
// oops, ctor threw an exception
operator delete(raw, pool); // that's the magical line!!
throw; // rethrow the ctor's exception
}
In other words, the one-liner function operator delete(void* p, Pool& pool) causes the compiler
to automagically plug the memory leak. Of course that function can be, but doesn't have to be,
inline.
Problems #2 ("ugly therefore error prone") and #3 ("users must manually associate pool-pointers
with the object that allocated them, which is error prone") are solved simultaneously with an
additional 10-20 lines of code in one place. In other words, we add 10-20 lines of code in one
place (your Pool header file) and simplify an arbitrarily large number of other places (every
piece of code that uses your Pool class). 
The idea is to implicitly associate a Pool* with every allocation. The Pool* associated with the
global allocator would be NULL, but at least conceptually you could say every allocation has an
associated Pool*. Then you replace the global operator delete so it looks up the associated Pool*,
and if non-NULL, calls that Pool's deallocate function. For example, if(!) the normal deallocator
used free(), the replacment for the global operator delete would look something like this:
void operator delete(void* p)
{
if (p != NULL) {
Pool* pool = /* somehow get the associated 'Pool*' */;
if (pool == null)
free(p);
else
pool->dealloc(p);
}
}
If you're not sure if the normal deallocator was free(), the easiest approach is also replace the
global operator new with something that uses malloc(). The replacement for the global operator
new would look something like this (note: this definition ignores a few details such as the
new_handler loop and the throw std::bad_alloc() that happens if we run out of memory):
void* operator new(size_t nbytes)
{
if (nbytes == 0)
nbytes = 1; // so all alloc's get a distinct address
void* raw = malloc(nbytes);
...somehow associate the NULL 'Pool*' with 'raw'...
return raw;
}
The only remaining problem is to associate a Pool* with an allocation. One approach, used in at
least one commercial product, is to use a std::map. In other words, build a look-up table whose
keys are the allocation-pointer and whose values are the associated Pool*. For reasons I'll
describe in a moment, it is essential that you insert a key/value pair into the map only in operator
new(size_t,Pool&). In particular, you must not insert a key/value pair from the global operator
new (e.g., you must not say, poolMap[p] = NULL in the global operator new). Reason: doing
that would create a nasty chicken-and-egg problem since std::map probably uses the global
operator new, it ends up inserting a new entry every time inserts a new entry, leading to infinite
recursion bang you're dead.
Even though this technique requires a std::map look-up for each deallocation, it seems to have
acceptable performance, at least in many cases.
Another approach that is faster but might use more memory and is a little trickier is to prepend a
Pool* just before all allocations. For example, if nbytes was 24, meaning the caller was asking to
allocate 24 bytes, we would allocate 28 (or 32 if you think the machine requires 8-byte alignment
for things like doubles and/or long longs), stuff the Pool* into the first 4 bytes, and return the
pointer 4 (or 8) bytes from the beginning of what you allocated. Then your global operator delete
backs off the 4 (or 8) bytes, finds the Pool*, and if NULL, uses free() otherwise calls pool->dealloc(). The parameter passed to free() and pool->dealloc() would be the pointer 4 (or 8)
bytes to the left of the original parameter, p. If(!) you decide on 4 byte alignment, your code
would look something like this (although as before, the following operator new code elides the
usual out-of-memory handlers):
void* operator new(size_t nbytes)
{
if (nbytes == 0)
nbytes = 1; // so all alloc's get a distinct address
void* ans = malloc(nbytes + 4); // overallocate by 4 bytes
*(Pool**)ans = NULL; // use NULL in the global new
return (char*)ans + 4; // don't let users see the Pool*
}
void* operator new(size_t nbytes, Pool& pool)
{
if (nbytes == 0)
nbytes = 1; // so all alloc's get a distinct address
void* ans = pool.alloc(nbytes + 4); // overallocate by 4 bytes
*(Pool**)ans = &pool; // put the Pool* here
return (char*)ans + 4; // don't let users see the Pool*
}
void operator delete(void* p)
{
if (p != NULL) {
p = (char*)p - 4; // back off to the Pool*
Pool* pool = *(Pool**)p;
if (pool == null)
free(p); // note: 4 bytes left of the original p
else
pool->dealloc(p); // note: 4 bytes left of the original p
}
}
Naturally the last few paragraphs of this FAQ are viable only when you are allowed to change
the global operator new and operator delete. If you are not allowed to change these global
functions, the first three quarters of this FAQ is still applicable.

Q: How does free know the size of memory to be deleted.?
int *i = (int *)malloc(12); followed by free(i); how did free function call know how much of
memory to delete?
A: It depends on the implementation, but there is usually a malloc header added to all the
memory allocated through malloc. on Linux its 4 bytes of memory preceding the memory
returned to you, whihc contains the number of bytes allocated + 4(itself).
so when you say,
int *i = (int *)malloc(12);
it allocates 16 bytes.
-------------------17 |  | | |
-------------------^
|
i
As you can see above total of 16 bytes are allocated, first 4 bytes stores the number of bytes
allocated(offset to the next memory from the start of this memory).
address of the 5th byte is returned to i. now i can access 12 bytes from this byte.

Q: How do I allocate multidimensional arrays using new?A: There are many ways to do this, depending on how flexible you want the array sizing to be.
On one extreme, if you know all the dimensions at compile-time, you can allocate
multidimensional arrays statically (as in C):
class Fred { /*...*/ };
void someFunction(Fred& fred);
void manipulateArray()
{
const unsigned nrows = 10; // Num rows is a compile-time constant
const unsigned ncols = 20; // Num columns is a compile-time constant
Fred matrix[nrows][ncols];
for (unsigned i = 0; i < nrows; ++i) {
for (unsigned j = 0; j < ncols; ++j) {
// Here's the way you access the (i,j) element:
someFunction( matrix[i][j] );
// You can safely "return" without any special delete code:
if (today == "Tuesday" && moon.isFull())
return; // Quit early on Tuesdays when the moon is full
}
}
// No explicit delete code at the end of the function either
}
More commonly, the size of the matrix isn't known until run-time but you know that it will be
rectangular. In this case you need to use the heap ("freestore"), but at least you are able to
allocate all the elements in one freestore chunk.
void manipulateArray(unsigned nrows, unsigned ncols)
{
Fred* matrix = new Fred[nrows * ncols];
// Since we used a simple pointer above, we need to be VERY
// careful to avoid skipping over the delete code.
// That's why we catch all exceptions:
try {
// Here's how to access the (i,j) element:
for (unsigned i = 0; i < nrows; ++i) {
for (unsigned j = 0; j < ncols; ++j) {
someFunction( matrix[i*ncols + j] );
}
}
// If you want to quit early on Tuesdays when the moon is full,
// make sure to do the delete along ALL return paths:
if (today == "Tuesday" && moon.isFull()) {
delete[] matrix;
return;
}
...insert code here to fiddle with the matrix...
}
catch (...) {
// Make sure to do the delete when an exception is thrown:
delete[] matrix;
throw; // Re-throw the current exception
}
// Make sure to do the delete at the end of the function too:
delete[] matrix;

Finally at the other extreme, you may not even be guaranteed that the matrix is rectangular. For
example, if each row could have a different length, you'll need to allocate each row individually.
In the following function, ncols[i] is the number of columns in row number i, where i varies
between 0 and nrows-1 inclusive.
void manipulateArray(unsigned nrows, unsigned ncols[])
{
typedef Fred* FredPtr;
// There will not be a leak if the following throws an exception:
FredPtr* matrix = new FredPtr[nrows];
// Set each element to NULL in case there is an exception later.
// (See comments at the top of the try block for rationale.)
for (unsigned i = 0; i < nrows; ++i)
matrix[i] = NULL;
// Since we used a simple pointer above, we need to be
// VERY careful to avoid skipping over the delete code.
// That's why we catch all exceptions:
try {
// Next we populate the array. If one of these throws, all
// the allocated elements will be deleted (see catch below).
for (unsigned i = 0; i < nrows; ++i)
matrix[i] = new Fred[ ncols[i] ];
// Here's how to access the (i,j) element:
for (unsigned i = 0; i < nrows; ++i) {
for (unsigned j = 0; j < ncols[i]; ++j) {
someFunction( matrix[i][j] );
}
}
// If you want to quit early on Tuesdays when the moon is full,
// make sure to do the delete along ALL return paths:
if (today == "Tuesday" && moon.isFull()) {
for (unsigned i = nrows; i > 0; --i)
delete[] matrix[i-1];
delete[] matrix;
return;
}
...insert code here to fiddle with the matrix...
}
catch (...) {
// Make sure to do the delete when an exception is thrown:
// Note that some of these matrix[...] pointers might be
// NULL, but that's okay since it's legal to delete NULL.
for (unsigned i = nrows; i > 0; --i)
delete[] matrix[i-1];
delete[] matrix;
throw; // Re-throw the current exception
}
// Make sure to do the delete at the end of the function too.
// Note that deletion is the opposite order of allocation:
for (unsigned i = nrows; i > 0; --i)
delete[] matrix[i-1];
delete[] matrix;
}
Note the funny use of matrix[i-1] in the deletion process. This prevents wrap-around of the
unsigned value when i goes one step below zero.
Finally, note that pointers and arrays are evil. It is normally much better to encapsulate your
pointers in a class that has a safe and simple interface. The following FAQ shows how to do this.

Q: Can I free() pointers allocated with new? Can I delete pointers allocated with malloc()?A: No!
It is perfectly legal, moral, and wholesome to use malloc() and delete in the same program, or to
use new and free() in the same program. But it is illegal, immoral, and despicable to call free()
with a pointer allocated via new, or to call delete on a pointer allocated via malloc().
Beware! I occasionally get e-mail from people telling me that it works OK for them on machine
X and compiler Y. Just because they don't see bad symptoms in a simple test case doesn't mean it
won't crash in the field. Even if they know it won't crash on their particular compiler doesn't
mean it will work safely on another compiler, another platform, or even another version of the
same compiler.
Beware! Sometimes people say, "But I'm just working with an array of char." Nonetheless do not
mix malloc() and delete on the same pointer, or new and free() on the same pointer! If you
allocated via p = new char[n], you must use delete[] p; you must not use free(p). Or if you
allocated via p = malloc(n), you must use free(p); you must not use delete[] p or delete p! Mixing
these up could cause a catastrophic failure at runtime if the code was ported to a new machine, a
new compiler, or even a new version of the same compiler.
You have been warned.

Q: Why should I use new instead of trustworthy old malloc()? A: Constructors/destructors, type safety, overridability.
Constructors/destructors: unlike malloc(sizeof(Fred)), new Fred() calls Fred's constructor.
Similarly, delete p calls *p's destructor.
Type safety: malloc() returns a void* which isn't type safe. new Fred() returns a pointer of the
right type (a Fred*).
Overridability: new is an operator that can be overridden by a class, while malloc() is not
overridable on a per-class basis.

Q: Can I use realloc() on pointers allocated via new?A: No!
When realloc() has to copy the allocation, it uses a bitwise copy operation, which will tear many
C++ objects to shreds. C++ objects should be allowed to copy themselves. They use their own
copy constructor or assignment operator.
Besides all that, the heap that new uses may not be the same as the heap that malloc() and
realloc() use!

Q: Do I need to check for NULL after p = new Fred()?A: No! (But if you have an old compiler, you may have to force the new operator to throw an
exception if it runs out of memory.)
It turns out to be a real pain to always write explicit NULL tests after every new allocation. Code
like the following is very tedious:
Fred* p = new Fred();
if (p == NULL)
throw std::bad_alloc();
If your compiler doesn't support (or if you refuse to use) exceptions, your code might be even
more tedious:
Fred* p = new Fred();
if (p == NULL) {
std::cerr << "Couldn't allocate memory for a Fred" << std::endl;
abort();
}
Take heart. In C++, if the runtime system cannot allocate sizeof(Fred) bytes of memory during p
= new Fred(), a std::bad_alloc exception will be thrown. Unlike malloc(), new never returns
NULL!
Therefore you should simply write:
Fred* p = new Fred(); // No need to check if p is NULL
However, if your compiler is old, it may not yet support this. Find out by checking your
compiler's documentation under "new". If you have an old compiler, you may have to force the
compiler to have this behavior.
Note: If you are using Microsoft Visual C++, to get new to throw an exception when it fails you
must #include some standard header in at least one of your .cpp files. For example, you could
#include (or or or ...).

Q: How can I convince my (older) compiler to automatically check new to see if it returns
NULL?
A: Eventually your compiler will.
If you have an old compiler that doesn't automagically perform the NULL test, you can force the
runtime system to do the test by installing a "new handler" function. Your "new handler"
function can do anything you want, such as throw an exception, delete some objects and return
(in which case operator new will retry the allocation), print a message and abort() the program,
etc.
Here's a sample "new handler" that prints a message and throws an exception. The handler is
installed using std::set_new_handler():
#include // To get std::set_new_handler
#include // To get abort()
#include // To get std::cerr
class alloc_error : public std::exception {
public:
alloc_error() : exception() { }
};
void myNewHandler()
{
// This is your own handler. It can do anything you want.
throw alloc_error();
}
int main()
{
std::set_new_handler(myNewHandler); // Install your "new handler"
...
}
After the std::set_new_handler() line is executed, operator new will call your myNewHandler()
if/when it runs out of memory. This means that new will never return NULL:
Fred* p = new Fred(); // No need to check if p is NULL
Note: If your compiler doesn't support exception handling, you can, as a last resort, change the
line throw ...; to:
std::cerr << "Attempt to allocate memory failed!" << std::endl;
abort();
Note: If some global/static object's constructor uses new, it won't use the myNewHandler()
function since that constructor will get called before main() begins. Unfortunately there's no
convenient way to guarantee that the std::set_new_handler() will be called before the first use of
new. For example, even if you put the std::set_new_handler() call in the constructor of a global
object, you still don't know if the module ("compilation unit") that contains that global object
will be elaborated first or last or somewhere inbetween. Therefore you still don't have any
guarantee that your call of std::set_new_handler() will happen before any other global's
constructor gets invoked.

Q: Do I need to check for NULL before delete p?A: No!
The C++ language guarantees that delete p will do nothing if p is equal to NULL. Since you
might get the test backwards, and since most testing methodologies force you to explicitly test
every branch point, you should not put in the redundant if test.
Wrong:
if (p != NULL)
delete p;
Right:
delete p;

Q: What are the two steps that happen when I say delete p?A: N delete p is a two-step process: it calls the destructor, then releases the memory. The code
generated for delete p is functionally similar to this (assuming p is of type Fred*):
// Original code: delete p;
if (p != NULL) {
p->~Fred();
operator delete(p);
}
The statement p->~Fred() calls the destructor for the Fred object pointed to by p.
The statement operator delete(p) calls the memory deallocation primitive, void operator
delete(void* p). This primitive is similar in spirit to free(void* p). (Note, however, that these two
are not interchangeable; e.g., there is no guarantee that the two memory deallocation primitives
even use the same heap!)

Q: In p = new Fred(), does the Fred memory "leak" if the Fred constructor throws an exception?A: No.
If an exception occurs during the Fred constructor of p = new Fred(), the C++ language
guarantees that the memory sizeof(Fred) bytes that were allocated will automagically be released
back to the heap.
Here are the details: new Fred() is a two-step process:
sizeof(Fred) bytes of memory are allocated using the primitive void* operator new(size_t
nbytes). This primitive is similar in spirit to malloc(size_t nbytes). (Note, however, that these
two are not interchangeable; e.g., there is no guarantee that the two memory allocation primitives
even use the same heap!).
It constructs an object in that memory by calling the Fred constructor. The pointer returned from
the first step is passed as the this parameter to the constructor. This step is wrapped in a try ...
catch block to handle the case when an exception is thrown during this step.
Thus the actual generated code is functionally similar to:
// Original code: Fred* p = new Fred();
Fred* p = (Fred*) operator new(sizeof(Fred));
try {
new(p) Fred(); // Placement new
}
catch (...) {
operator delete(p); // Deallocate the memory
throw; // Re-throw the exception
}
The statement marked "Placement new" calls the Fred constructor. The pointer p becomes the
this pointer inside the constructor, Fred::Fred().

Q: How do I allocate / unallocate an array of things?A: Use p = new T[n] and delete[] p:
Fred* p = new Fred[100];
...
delete[] p;
Any time you allocate an array of objects via new (usually with the [n] in the new expression),
you must use [] in the delete statement. This syntax is necessary because there is no syntactic
difference between a pointer to a thing and a pointer to an array of things (something we
inherited from C).

Q: What if I forget the [] when deleteing array allocated via new T[n]?A: All life comes to a catastrophic end.
It is the programmer's not the compiler's responsibility to get the connection between new T[n]
and delete[] p correct. If you get it wrong, neither a compile-time nor a run-time error message
will be generated by the compiler. Heap corruption is a likely result. Or worse. Your program
will probably die.

Q: Can I drop the [] when deleteing array of some built-in type (char, int, etc)?A: No!
Sometimes programmers think that the [] in the delete[] p only exists so the compiler will call the
appropriate destructors for all elements in the array. Because of this reasoning, they assume that
an array of some built-in type such as char or int can be deleted without the []. E.g., they assume
the following is valid code:
void userCode(int n)
{
char* p = new char[n];
...
delete p; //  ERROR! Should be delete[] p !
}
But the above code is wrong, and it can cause a disaster at runtime. In particular, the code that's
called for delete p is operator delete(void*), but the code that's called for delete[] p is operator
delete[](void*). The default behavior for the latter is to call the former, but users are allowed to
replace the latter with a different behavior (in which case they would normally also replace the
corresponding new code in operator new[](size_t)). If they replaced the delete[] code so it wasn't
compatible with the delete code, and you called the wrong one (i.e., if you said delete p rather
than delete[] p), you could end up with a disaster at runtime.

Q: After p = new Fred[n], how does the compiler know there are n objects to be destructed
during delete[] p?
A: Short answer: Magic.
Long answer: The run-time system stores the number of objects, n, somewhere where it can be
retrieved if you only know the pointer, p. There are two popular techniques that do this. Both
these techniques are in use by commercial-grade compilers, both have tradeoffs, and neither is
perfect. These techniques are:
Over-allocate the array and put n just to the left of the first Fred object.
Use an associative array with p as the key and n as the value.

Q: Is it legal (and moral) for a member function to say delete this?A: As long as you're careful, it's OK for an object to commit suicide (delete this). 
Here's how I define "careful":
You must be absolutely 100% positive sure that this object was allocated via new (not by new[],
nor by placement new, nor a local object on the stack, nor a global, nor a member of another
object; but by plain ordinary new).
You must be absolutely 100% positive sure that your member function will be the last member
function invoked on this object.
You must be absolutely 100% positive sure that the rest of your member function (after the
delete this line) doesn't touch any piece of this object (including calling any other member
functions or touching any data members).
You must be absolutely 100% positive sure that no one even touches the this pointer itself after
the delete this line. In other words, you must not examine it, compare it with another pointer,
compare it with NULL, print it, cast it, do anything with it.
Naturally the usual caveats apply in cases where your this pointer is a pointer to a base class
when you don't have a virtual destructor.

PREVIOUS                                                                                                                       NEXT

No comments:

Post a Comment