Когда класс динамически размещает себя в конструкторе, почему переполнение стека происходит вместо std :: bad_alloc? - PullRequest
13 голосов
/ 11 июня 2019

Я создал класс, который рекурсивно создает себя, используя new (просто для удовольствия!), Ожидая, что это вызовет std::bad_alloc из-за бесконечного динамического выделения (переполнение кучи). Но переполнение стека произошло вместо std::bad_alloc. Почему это происходит?

class Overflow
{
private:
    Overflow* overflow;

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

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

@ Калет спросил, что произойдет, если я заменю новый Overflow () на новый Overflow [100000], и это дало мне std::bad_alloc. Согласно ответам, не должно ли это также привести к переполнению стека?

Ответы [ 3 ]

18 голосов
/ 11 июня 2019

Переполнение стека происходит из-за бесконечной рекурсии. Вызов Overflow() заставляет вас звонить Overflow() снова, снова и снова. Эти вызовы функций должны идти в стек. Поскольку ваш стек меньше вашей кучи, вам не хватит места в стеке для всех этих вызовов конструктора, прежде чем вам не хватит памяти для всех создаваемых вами объектов.

4 голосов
/ 11 июня 2019

Поскольку вы рекурсивно вызываете конструктор, метод, несколько раз.Вызовы методов переносятся в стек вызовов.Поскольку размер стека намного меньше доступной кучи, стек вызовов переполняется до того, как заканчивается куча.

1 голос
/ 13 июня 2019

Я сделал небольшую модификацию вашего кода:

#include <array>

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

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

Вкл. wandbox это

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

приводит к ошибке сегментации, вызванной переполнением стека.

Однако это

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

результат в

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

Aborted

У вас здесь два конкурирующих эффекта. В первом приближении (подробности немного сложнее) вы используете для каждой рекурсии констурктора:

  • a Overflow* в стеке
  • целый Overflow экземпляр в куче

Следовательно, если вы сначала получите переполнение стека или bad_alloc, зависит от размера Overflow. А для небольших размеров вы сначала получите переполнение, потому что пространство стека намного более ограничено, чем пространство кучи.

PS: Я пропустил ваше редактирование ... если вы поместите new Overflow[100000] в конструктор в своем коде, вы увеличите требуемое пространство кучи, как я сделал, добавив элемент array. В стеке у вас все еще есть один указатель, и поэтому у вас заканчивается куча гораздо раньше.

...