Подтвердить что ты не робот

Стек против кучи С++

У меня просто был быстрый вопрос о том, как работают переменные стека и переменные кучи. Насколько я понимаю, переменными стека являются переменные, которые после возвращения функций будут удалены, а переменные кучи будут постоянными. Но я действительно смущен, так это то, как распределять переменные кучи внутри функций:

int MyObject::addObject(const char* a){
    MyObject newObject(a);
    return 0;
}

Скажем, что у меня есть конструктор для MyObject, который newObject(const char * a). Затем в этой функции, когда она вызывается, после возврата создается ли вновь созданный newObject? Если да, как вы можете выделить кучу внутри функции? Если нет, как вы очистите свою память позже?

Кроме того, какова роль деструктора и когда он называется?

4b9b3361

Ответ 1

Конструктор для класса MyObject равен MyObject(), а не newObject(). В вашем примере newObject - это имя вашей переменной, а не конструктор.

Чтобы выделить кучу внутри функции, вам нужно вызвать оператор new:

int MyObject::addObject(const char* a){
    MyObject* newObject = new MyObject(a);
    //newObject is allocated on the heap

    //... Some more code...

    delete newObject;
    //you have to explicitly delete heap-allocated variables
    //if not, you get memory leaks
    return 0;
}

Ваш код:

MyObject newObject(a);

создает автоматическое хранилище MyObject, называемое newObject, которое живет до тех пор, пока область действия не будет объявлена ​​в конце, т.е. закрывается }.

Кроме того, какова роль деструктора и когда он называется?

Чтобы очистить память, класс, выделенный с помощью new или new[] (или malloc), и он принадлежит. Он вызывается либо, когда объект выходит за пределы области для автоматических объектов, либо когда вы явно вызываете delete для динамических объектов.

Ответ 2

Память стеков - это то, что вы сказали, но, более конкретно, это локальная память для текущей области. Обратите внимание, что в зависимости от того, в какой области вы говорите, эта память может быть глобальной или может быть локальной для функции или класса. Они очищаются после того, как они вышли из сферы действия.

Память кучи, выделенная с помощью оператора new. Они сохраняются в памяти даже после того, как все указатели, указывающие на память, вышли за рамки. Вы должны явно освободить эту память самостоятельно на С++ через delete. В противном случае произойдет утечка памяти, если не будут использованы интеллектуальные указатели.

Еще одно замечание: возьмите следующий пример кода:

void foo()
{
   int *bar = new int; //creates a pointer to an int on the stack, but initializes an int on the heap
   *bar = 3; //sets the int being pointed to to 3
   delete bar; //deletes the int
   bar = 0; //removes the now invalid reference
}

сам указатель, bar, создается в стеке. Однако данные, на которые указывает бар. * bar - да, звездочка действительно имеет значение здесь. Это дает вам данные, на которые указывает указатель - находится в куче. Когда возвращается foo, бар выйдет за рамки. Если бы я не вызвал delete, я бы потерял доступ к * bar и получил бы утечку памяти.

Во втором вопросе роль dtor заключается в очистке объекта, который вы создали. Один предоставляется по умолчанию для вас, но есть моменты, когда вам нужно писать свои собственные. Например, если в вашем классе есть необработанные указатели, вам необходимо явно освободить их с удалением в dtor. Он вызывается, когда объект выходит за пределы области памяти стека, а когда вызывается удаление для памяти кучи.

Ответ 3

int addObject(const char* a){
    MyObject newObject(a);
    return 0;
}

Этот код выделяет newObject в стеке. Он создается (конструктор MyObject вызывается на куске памяти стека) при вводе функции. Он разрушен (MyObject деструктор, вызываемый в этом экземпляре), когда функция возвращается непосредственно перед выделением куска памяти из стека.

MyObject *foo =  new MyObject(a);

Этот код выделяет объект в куче. Конструктор вызывается новым оператором после того, как он выделил некоторую кучную память. Память не освобождается, и деструктор не вызывается до тех пор, пока вы явно не скажете, что вы сделали это с ключевым словом delete.

delete foo;

Это может быть где угодно в вашей программе.

Ответ 4

  • Стек - это область, которая используется в вызовах функций и методов. Он в основном используется для хранения параметров и работы с локальными данными (среди прочего, связанных с процессом вызова функции). Стек представляет собой структуру данных LIFO, что означает, что последнее, что вы положили, - это первое, что вы получили от него.

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

  • В С++ каждый раз, когда вы вызываете new, объект создается в куче, и вам возвращается указатель на этот объект, вы можете использовать этот указатель для доступа к вашему объекту. Это касается как сложных объектов, таких как объекты из классов, которые вы или кто-то создал, так и встроенных типов, таких как int, char, double и т.д.... Нет ничего волшебного в использовании нового, на нем ядро ​​оно просто вызывает malloc, или какой-либо вкус его в зависимости от платформы, а затем вызывает конструктор для объекта. Вы должны помнить, что вы вызываете delete (или delete []p_array_of_object_pointers) для каждого объекта, созданного с помощью new.

  • Деструктор - это не что иное, как функция, которую язык вызывает объект, когда вы выполняете delete my_object_pointer в своем коде. Деструктор дает вам возможность немного очистить свой код. В таких языках, как С++, где вы должны управлять своими собственными ресурсами, вы обычно используете его для вызова delete или free для других объектов, которые вы выделили в куче, или закрытия файлов, которые вы создали с помощью fopen или такие...

  • Оба этих ключевых слова работают внутри или вне функций и методов, неважно, откуда вы их вызываете, они всегда будут работать с кучей. Все, что вы создаете без использования new, попадет в другое место, либо в статическую область исполняемого файла, либо в стек, в зависимости от того, как и где он создается.

  • Каждый раз, когда вы создаете объект внутри метода или функции со следующим синтаксисом MyClassType myObject(contructor, arguments); и без использования new, вы создаете его в стеке (если вы не объявите его как статический, когда он заканчивается в той статической области, о которой я упоминал выше).

Надеюсь, что это поможет.

Ответ 5

распределения кучи выполняются с помощью ключевого слова new

С++ не содержит сборку мусора, распределения кучи никогда не будут автоматически освобождены

вы можете освободить их с помощью ключевого слова delete

вам нужно сохранить указатель на это, например, вернув его из функции или назначив ему глобальную переменную

деструкторы вызываются на delete и используются для освобождения ресурсов, например сетевых сокетов

поэтому код будет, например, return new MyObject("foo");

Ответ 6

Вы выделяете память в стеке в своем примере. Это автоматическая переменная, и она будет удалена, а затем произойдет возврат. Если вы используете ключевое слово new, вы должны предоставить правильное освобождение

MyObject* newObject = new MyObject(a);

Ответ 7

Как выделить кучу: используйте new ключевое слово

Как освободить из кучи: используйте ключевое слово delete.

Чтобы избежать сбоев в программе и утечек памяти, каждый new должен иметь свою копию delete для указанного объекта и типа.

Деструктор вызывается, когда объект удаляется независимо от того, где он был сохранен (куча, стек,...)

А вот более конкретная информация о памяти: Что и где находится стек и куча?

Ответ 8

Использовать динамически распределенные экземпляры объектов, когда:

1) Фактический тип объекта неизвестен во время компиляции 2) Когда количество объектов, которые вы хотите создать, не известно во время компиляции 3) Когда управление жизненным циклом объекта (сгенерированное компилятором) не отвечает вашим потребностям.

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

Когда объект создается как переменная стека, компилятор будет вставлять код для вызова деструктора объектов, когда объект выходит из области видимости. Как компилятор знает, что деструктор для вызова? Тип объекта известен во время компиляции, поэтому есть только одна возможность, только один деструктор для вызова.

Если вы используете "delete" для уничтожения объекта, компилятор может не знать, какой деструктор использовать. Объект может иметь тип "basic", затем вызывается деструктор "basic:: ~ basic()".

    class basic;
    class derived : public basic
    {
           ....
           ~derived();
    }

    basic *l = new derived();
    ...
    delete l;

Компилятор не знает, что фактический тип объекта не является "базовым", а "производным". Если, однако, вы объявляете деструктор "basic" виртуальным, компилятор вставляет код, который обращается к таблице виртуальных методов объектов, а вызов перенаправляется в правый деструктор (в этом случае деструктор, который разрушает объект типа " полученный ').

Если вы беспокоитесь об этой проблеме, и если хотите, сделайте деструктор "виртуальным".