[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Sv: Sv: Sv: Memory allocators



>>catch the exception anyway. This is what I see as getting both utility and
>>functionality "for free".
>
>In theory this is right, but in a function like this one:
>
>int SomeFunc (void)
>{
>  char *String1 = new [256];
>  char *String2 = new [1024];
>
>  // do something
>
>  delete[] String1;
>  delete[] String2;
>}
>
>(just a silly example. Imagine a function that really needs to allocate the
>stuff dynamically).
>If the first allocation here fails, everything is fine. But if the second
>one fails we have a memory leak.
>So you have to check for errors in many cases. The problem is even greater
>if the function calls another allocating function instead of the second
>allocation - in such a case ommitting the neccessary check is all too easy.
>
>So I check every allocation for failures - and in this case checking for a
>zero return value is simpler than catching bad_alloc.

Consider this:

template<class TYPE>
class delPtr
{
    ~delPtr()
        {delete m_pPtr;}
// declare all operators  one-line inline functions that makes
// delPtr behave as a real pointer to an object of type TYPE.
protected:
    TYPE* m_pPtr;
}

int SomeFunc (void)
{
  delPtr<char> pStr1 = new char[256];
  delPtr<char> pStr2 = new char[256];

  // do something
}

Notice that you don't even need the deletes. The compiler will generate
calls to the destructor of pStr1 and pStr2 whenever the function exits,
nomatter wheter the cause is an exception or not.

In some cases, one might not wish to have the memory allocated deleted. In
that case, it's possible to set the value of the object of type delPtr to 0
(NULL).

Also, I really don't see what's so bad about this:

int SomeFunc (void)
{
  char* pStr1 = 0;
  char* pStr2 = 0;

  try
  {
    pStr1 = new char[256];
    pStr2 = new char[256];
  }
  catch (...)
  {
    delete[] pStr1;
    delete[] pStr2;
    throw;
  }

  // do something

  delete[] String1;
  delete[] String2;
}

I don't see how its THAT much better than this:

int SomeFunc (void)
{
  pStr1 = new char[256];
  if (pStr1 == 0)
    return;

  pStr2 = new char[256];
  if (pStr2 == 0)
  {
    delete pStr1;
    return;
  }

  // do something

  delete[] String1;
  delete[] String2;
}

Ok, the first is a little longer. Its also, atleast to me, nicer to look at.
Its clear what's being done, as all "functionality-code" is placed in one
block of code. All error-handling code is placed on one block.

However, to me, these are merely auxillary arguments. The real and best
argument is simply that throwing bad_alloc is what's supposed to happen. A
library not built around this is IMHO not a well-behaved library. Also, the
only way to seamlessly integrate with anything else is to follow the
standards. Simple as that.

>>Nomatter what is decided, getting everything to function uniformly is not
>>harder than calling _set_new_handler() with our own handler that does what
>>we want it to do.
>
>But then problems arise when the user uses PPlay code together with another
>library which also sets its custom _new_handler ().
>
PPlay should only be aimed at being used together with external code that is
well-behaved. If other code installs a custom _new_hander(), then that
new-handler, nomatter what else it migth do, should throw bad_alloc. Else
its not well-behaved, as throwing bad_alloc is what's supposed to happen at
failed allocation.

I migth have misunderstod this, but if we place some code into a namespace,
and define our own, within the namespace, global new() function, then we can
implement it like this, without affection any other namespace's new
functions.

void* new(size_t size)
{
    static bad_alloc excep; // can't allocate this if there's no memory...
                                            // so it has to be static
    void* pMem = someOtherNamespaceWithNoNewDefined::new(size);
    if (pMem == 0)
        throw excep;
    return pMem;
}

This isn't very effecient if new already throws bad_alloc at allocation
failure. The simple fix is to make it dependent on a preprocessor symbol
wheter or not this custom new gets included in the namespace.

Anyways, the main point is that using bad_alloc is the C++ standard, and not
to use it therefore must logically be bad behavior. Also, I suspect that the
people who define the standards know what they are doing... ;)