When a class dynamically allocates itself at constructor, why does stack overflow happen instead of std::bad_alloc?

Viewed 396

I made a class that recursively creates itself using new (just for fun!), expecting that this will throw std::bad_alloc due to infinite dynamic allocation (heap overflow). But stack overflow happened instead of std::bad_alloc. Why does this happen?

class Overflow
{
private:
    Overflow* overflow;

public:
    Overflow()
    {
        overflow = new Overflow();
    }
};

int main()
{
    Overflow overflow_happens; // stack overflow happens instead of std::bad_alloc exeption
}

@Caleth asked what happens if I change new Overflow() to new Overflow[100000], and this gave me std::bad_alloc. According to the answers, shouldn't this also give me stack overflow?

3 Answers

The stack overflow is happening because you have infinite recursion. Calling Overflow() causes you to call Overflow() again and again and again. Those function calls need to go on the stack. Since your stack is smaller than your heap you'll run out of stack space for all of the those constructor calls before you run out of memory for all of the objects you are creating.

Because you are recursively calling the constructor, a method, repeatedly. The method calls get pushed to the call stack. Since the stack size is much smaller than the available heap, the call stack overflows before the heap runs out.

I made a small modification to your code:

#include <array>

template <size_t size>
class Overflow
{
private:
    Overflow* overflow;
    std::array<int,size> x;

public:
    Overflow()
    {
        overflow = new Overflow();
    }
};

On wandbox this

int main()
{
    Overflow<1> overflow_happens;
}

results in a segmentation fault caused by overflow of the stack.

However, this

int main()
{    
    Overflow<10000> bad_alloc;
}

results in

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc

Aborted

You basically have two competing effects here. As a first approximation (details are a bit more involved) you have for each recursion of the consturctor:

  • a Overflow* on the stack
  • a whole Overflow instance on the heap

Hence whether you first get a stack overflow or bad_alloc depends on the size of Overflow. And for small sizes you'll first get a overflow, because stack space is much more limited than heap space.

PS: I missed your edit... if you place new Overflow[100000] in the constructor in your code you amplify the required heap space, just as I did by adding the array member. On the stack you still have a single pointer and hence you run out of heap much earlier.

Related