Выделяется ли этот тип памяти в куче или стеке?

В контексте С++ (не то, чтобы это имело значение):

class Foo{
    private:
        int x[100];
    public:
        Foo();
}

То, что я узнал, говорит мне, что если вы создадите экземпляр Foo следующим образом:

Foo bar = new Foo();

Затем массив x выделяется в куче, но если вы создали экземпляр Foo следующим образом:

Foo bar;

Затем он создается в стеке.

Я не могу найти ресурсы в Интернете, чтобы подтвердить это.


person Majd Taby    schedule 09.01.2009    source источник


Ответы (5)


Учитывая небольшую модификацию вашего примера:

class Foo{
    private:
        int x[100];
        int *y;
    public:
        Foo()
        {
           y = new int[100];
        }
        ~Foo()
        { 
           delete[] y; 
        }

}

Пример 1:

Foo *bar = new Foo();
  • x и y находятся в куче:
  • sizeof(Foo*) создается в стеке.
  • sizeof(int) * 100 * 2 + sizeof(int *) находится в куче

Пример 2:

Foo bar;
  • x находится в стеке, а y находится в куче
  • sizeof(int) * 100 находится в стеке (x) + sizeof(int*)
  • sizeof(int) * 100 находится в куче (y)

Фактические размеры могут немного отличаться из-за выравнивания класса/структуры в зависимости от вашего компилятора и платформы.

person Brian R. Bondy    schedule 09.01.2009
comment
Есть ли у вас какие-либо рекомендуемые ссылки на то, как вы пришли к этому выводу? У меня было такое же впечатление, как и у ОП; вызовы new динамически выделяют память в куче для данных. В противном случае вызов типа Foo bar поместит локальные данные в стек. В любом случае я бы ожидал, что указатель класса будет размещен строго в стеке. Как модификаторы public и private влияют на метод распределения? - person sherrellbc; 06.11.2013

Строго говоря, в соответствии со стандартом объект не обязательно должен находиться в стеке или куче. Стандарт определяет 3 типа «длительности хранения», но не указывает, как именно должно быть реализовано хранение:

  1. продолжительность статического хранения
  2. продолжительность автоматического хранения
  3. продолжительность динамического хранения

Автоматическая продолжительность хранения обычно (почти всегда) реализуется с использованием стека.

Продолжительность динамического хранения обычно реализуется с использованием кучи (в конечном итоге через malloc()), хотя это может быть переопределено даже пользователем компилятора.

Длительность статического хранения — это то, что обычно называют глобальными (или статическими хранилищами).

В стандарте говорится об этих вещах (ниже приведены выдержки из различных битов 3.7 - Продолжительность хранения):

Статическая и автоматическая продолжительность хранения связаны с объектами, введенными объявлениями (3.1) и неявно созданными реализацией (12.2). Длительность динамического хранения связана с объектами, созданными с помощью оператора new (5.3.4).

...

Все объекты, которые не имеют динамической продолжительности хранения и не являются локальными, имеют статическую продолжительность хранения. Хранение этих объектов должно продолжаться в течение всего времени работы программы (3.6.2, 3.6.3).

...

Локальные объекты, явно объявленные как auto или register, или не объявленные явно как static или extern, имеют автоматический срок хранения. Хранилище для этих объектов длится до тех пор, пока блок, в котором они созданы, не выйдет.

...

Объекты могут создаваться динамически во время выполнения программы (1.9), с использованием выражений new (5.3.4) и уничтожаться с помощью выражений удаления (5.3.5). Реализация C++ обеспечивает доступ к динамическому хранилищу и управление им с помощью глобальных функций выделения памяти оператора new и оператора new[] и глобальных функций освобождения памяти оператора delete и оператора delete[].

...

Библиотека предоставляет определения по умолчанию для функций глобального выделения и освобождения. Некоторые глобальные функции выделения и освобождения заменяемы (18.4.1).

И, наконец (относительно массива в вашем примере класса):

3.7.4 Продолжительность подобъектов [basic.stc.inherit]

Продолжительность хранения подобъектов-членов, подобъектов базового класса и элементов массива равна продолжительности их полного объекта (1.8).

person Michael Burr    schedule 09.01.2009

Объект типа Foo имеет размер 100 целых чисел, хранящихся последовательно. Если вы создадите его в стеке, вы получите все это в стеке. Если вы сделаете это с новым, он будет в куче как часть объекта.

Это часть спецификации языка, я не уверен, в чем ваш вопрос.

person Uri    schedule 09.01.2009
comment
может быть, он спрашивает, в какой части спецификации языка это сказано :-) - person PolyThinker; 09.01.2009

Да, массив элементов x будет создан в куче, если вы создадите объект Foo в куче. Когда вы выделяете динамическую память для Foo, вы запрашиваете память длиной sizeof(Foo) (плюс, возможно, некоторые накладные расходы памяти, но давайте пока проигнорируем это), что в вашем примере кода подразумевает размер 100 ints. Это должно иметь место, чтобы срок жизни объектов типа Foo (и их внутренних данных) пересекал области.

Если вы не создаете объект Foo в куче, а внутренний массив Foo не является указателем, которому вы выделяете память с помощью new в конструкторе Foo, тогда этот внутренний массив будет создан в стеке. Опять же, это должно быть так, чтобы массив автоматически очищался без каких-либо deletes, когда область действия заканчивается. Конкретно,

struct Foo {
    int* y;
    Foo() : y(new int()) { }
    ~Foo() { delete y; }
};

создаст y в куче независимо от того, был ли объект Foo создан в стеке или в куче.

person wilhelmtell    schedule 09.01.2009

Ты имеешь в виду

Foo* bar = new Foo(); 

Я полагаю. Это создается в куче.

person Otávio Décio    schedule 09.01.2009