2

I’m trying to implement a technique to test failing operator new described in http://www.codeproject.com/Articles/6108/Simulating-Memory-Allocation-Failure-for-Unit-Test.

This is a sample code being tested:

VArray* arr = new VArray(1U, 3U, true);

I can make new return NULL instead of allocating memory. In this case, the program should continue to next line (which should test if arr == NULL) and this is exactly what it does in MSVC.

However, the constructor of VArray is still called after the failed new in GCC. And since this is NULL, it results in SIGSEGV on the first assignment to a property. This seems to be a wrong behavior according to C++03 standard: https://stackoverflow.com/a/11514528/711006

My implementation of operators new and delete follows.

unsigned int OperatorPlainNewFailsAfter = UINT_MAX;

void* operator new(const size_t _size) throw()
{
  void* result;
  if(OperatorPlainNewFailsAfter == 0U)
  {
    result = NULL;
  }
  else
  {
    result = malloc(_size);
    OperatorPlainNewFailsAfter--;
  }
  return result;
}

void operator delete(void* _memory) throw()
{
  free(_memory);
}

What do I miss?

Community
  • 1
  • 1
Melebius
  • 6,183
  • 4
  • 39
  • 52
  • 7
    A replacement `operator new` must throw `std::bad_alloc` on failure. Returning `NULL` is a violation of contract. "Required behavior: Return a non-null pointer to suitably aligned storage (3.7.4), or else throw a `bad_alloc` exception. **This requirement is binding on a replacement version of this function**." – CB Bailey Oct 20 '14 at 08:50

2 Answers2

4

The C++ standard requires that an allocation function (e.g. the basic operator new) fails by throwing a std::bad_alloc exception, if it fails. It's not allowed to return a nullpointer. When you do return a nullpointer you have Undefined Behavior, and yes, one possible consequence is that a constructor is called.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Thanks for the tip but my original intention was to replace `new (std::nothrow)` and I agree I’ve done it half-way. So you helped me to finish it correctly. – Melebius Oct 20 '14 at 09:18
1

If the operator new being called is declared throw(), then it is an error for the compiler to not check for a null pointer. But it is illegal to declare a non-placement new throw(), so there's no point in the compiler checking it; if anything, it should complain when it sees the declaration, because the non-placement operator new is implicitly declared.

If you want to test for failing new (and it's a good idea), your operator new function shouldn't return a null pointer, but should throw std::bad_alloc. Otherwise, you're not testing the same thing. On the page you site, the test code uses a placement new instead. The code is still illegal, because his operator new may return a null pointer without having declared the function throw(), but that is easily fixed. (Or not. He calls ::operator new, rather than malloc, for the actual allocation, and this function may throw. He also calls the ::operator new function to allocate, but used the ::delete operator de free; if you call ::operator new to allocate, you should call ::operator delete to free.) Of course, even fixed, it doesn't test anything useful, since what you need to know is that your program reacts correctly to std::bad_alloc, not that it reacts correctly to a null pointer that it will never see.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Thank you for the thorough check of the original example. Yes, I modified it because placement `new` isn’t the thing I want to test. Since I’m replacing the “basic” global `new`, I cannot call `::operator new`, since it gets replaced! That’s the reason for `malloc` and `free`. – Melebius Oct 20 '14 at 09:28