В чем разница между free и delete в C++?

Ответ

free() и delete — это механизмы освобождения памяти, которые нельзя смешивать.

free() — функция стандартной библиотеки C (<cstdlib>). Она освобождает «сырую» память, выделенную функциями malloc(), calloc() или realloc(). free() не знает о типах C++ и не вызывает деструкторы объектов.

delete — оператор C++. Он выполняет две операции:

  1. Вызывает деструктор объекта (для освобождения ресурсов: закрытия файлов, освобождения другой памяти и т.д.).
  2. Освобождает память, выделенную для этого объекта оператором new.

Пример, демонстрирующий разницу:

#include <cstdlib>

class ResourceHolder {
    int* data;
public:
    ResourceHolder() { data = new int[100]; std::cout << "Resource allocatedn"; }
    ~ResourceHolder() { delete[] data; std::cout << "Resource freedn"; }
};

int main() {
    // C-стиль: НЕВЕРНО для объектов C++
    ResourceHolder* rh1 = (ResourceHolder*)malloc(sizeof(ResourceHolder));
    // Конструктор НЕ вызван! data не инициализирован.
    free(rh1); // Деструктор НЕ вызван! Утечка памяти для `data`.

    // C++-стиль: ПРАВИЛЬНО
    ResourceHolder* rh2 = new ResourceHolder(); // Вызывается конструктор
    delete rh2; // Вызывается деструктор (~ResourceHolder()), затем освобождается память

    return 0;
}

Важные правила:

  • Используйте delete для памяти, выделенной new.
  • Используйте delete[] для массивов, выделенных new[].
  • Используйте free() только для памяти, выделенной функциями семейства malloc.
  • Смешивание (new/free или malloc/delete) приводит к неопределенному поведению (undefined behavior).
  • В современном C++ предпочтительно использовать умные указатели (std::unique_ptr, std::shared_ptr) и контейнеры (std::vector), которые автоматически управляют памятью.

Ответ 18+ 🔞

Ну, вот смотри, тут такая тема, которую каждый второй на собеседовании пытается проехать, а потом в коде получается ёперный театр. free() и delete — это как водка и пиво: смешивать можно, но потом будет пиздец, и ты сам от себя охуеешь.

free() — это старый, добрый, тупой как пробка инструмент из C. Выделил память через malloc() — освобождай через free(). Он просто отдаёт операционке кусок памяти обратно и всё. Никаких тебе нежностей в виде вызова деструкторов. Просто взял и выкинул, как пустую бутылку в кусты.

delete — это уже наш, цивилизованный C++шный оператор. Он не просто память освобождает. Он сначала аккуратно так говорит объекту: «Дружище, ты своё отъебал, вызывай деструктор, закрывай файлы, чисти за собой хвосты». И только потом уже освобождает память.

Вот смотри на примере, тут всё станет ясно, как божий день:

#include <cstdlib>

class ResourceHolder {
    int* data;
public:
    ResourceHolder() { data = new int[100]; std::cout << "Resource allocatedn"; }
    ~ResourceHolder() { delete[] data; std::cout << "Resource freedn"; }
};

int main() {
    // Способ первый, криворукий. Делаем по-деревенски.
    ResourceHolder* rh1 = (ResourceHolder*)malloc(sizeof(ResourceHolder));
    // Конструктор-то не вызвался! Объект как сырой кусок мяса лежит. Память выделена, а `data` — хуй с горы, мусор.
    free(rh1); // И тут деструктор не вызовется! У нас же `free()`, ей похуй. В итоге массив `data` так и висит в памяти — вечная утечка. Красота, да?

    // Способ второй, правильный. Делаем как взрослые.
    ResourceHolder* rh2 = new ResourceHolder(); // Всё, конструктор отработал, память под массив выделена.
    delete rh2; // А вот тут сначала вызовется деструктор (~ResourceHolder()), который почистит массив, а потом уже освободится память под сам объект.

    return 0;
}

Короче, правила простые, их надо выжечь на подкорке:

  • Что выделил через new — убивай через delete.
  • Что выделил через new[] (массив) — убивай через delete[]. Перепутаешь — будет тебе не undefined behavior, а конкретная хиросима в рантайме.
  • Что выделил через malloc/calloc/realloc — освобождай через free().
  • Начинаешь мешать — получаешь неопределённое поведение, а это хуже, чем просто краш. Это когда вроде работает, а потом в пятницу вечером у заказчика всё накрывается медным тазом.
  • А самый правильный совет в 2023-м (ой, то есть в наше время) — вообще не ебаться с этим вручную. Бери std::unique_ptr, std::vector и живи спокойно. Пусть компилятор сам за тебя думает, где delete вызывать.