Sari la conținut

Dynamic Memory: new, delete & Their Dangers

Learn how dynamic allocation works with new and delete, understand the dangers of memory leaks, double-free, and use-after-free, and discover why modern C++ should contain zero raw new/delete.

Dynamic Allocation with new and delete

The stack is fast but limited in size and scope. When you need objects that outlive their creating scope, or whose size is unknown at compile time, you allocate on the heap (free store).

new T(args)allocates memory for oneT, calls its constructor, and returns aT*.delete pcalls the destructor and frees the memory. For arrays:new T[n]anddelete[] p. Mismatchingnewwithdelete[]ornew[]withdeleteis undefined behavior — the compiler will not catch this.

new, delete și variantele lor de matrice

Here is the raw allocation API. Study it so you recognize it in legacy code, but never write this in new code — use smart pointers instead.

new_delete.cpp
#include <iostream>
#include <cstring>

struct Widget {
    int id;
    Widget(int i) : id(i) { std::cout << "Widget " << id << " created\n"; }
    ~Widget() { std::cout << "Widget " << id << " destroyed\n"; }
};

int main() {
    // Single object
    Widget* w = new Widget(1);
    std::cout << "Using widget " << w->id << '\n';
    delete w;  // destructor called, memory freed

    // Array
    Widget* arr = new Widget[3]{{10}, {20}, {30}};
    for (int i = 0; i < 3; ++i)
        std::cout << arr[i].id << ' ';
    std::cout << '\n';
    delete[] arr;  // MUST use delete[] for new[]

    // UNDEFINED BEHAVIOR — don't do these:
    // delete arr;      // wrong: used delete instead of delete[]
    // delete[] w;      // wrong: used delete[] instead of delete
}

Placement new

Placement new constructs an object at a specific memory address without allocating. It is used by allocators, memory pools, and containers likestd::vectorinternally. You must call the destructor explicitly —deletemust NOT be used on placement-new'd objects.

placement_new.cpp
#include <iostream>
#include <new>       // required for placement new
#include <cstddef>   // std::byte
#include <memory>    // std::align

struct Sensor {
    int id;
    double reading;
    Sensor(int i, double r) : id(i), reading(r) {
        std::cout << "Sensor " << id << " init\n";
    }
    ~Sensor() { std::cout << "Sensor " << id << " shutdown\n"; }
};

int main() {
    // Pre-allocate raw storage (aligned)
    alignas(Sensor) std::byte buffer[sizeof(Sensor)];

    // Construct in pre-allocated memory
    Sensor* s = new (buffer) Sensor(42, 3.14);
    std::cout << "Reading: " << s->reading << '\n';

    // Must destroy explicitly — do NOT use delete
    s->~Sensor();  // explicit destructor call
    // delete s;    // WRONG — memory was not allocated by new
}

Cele trei erori mortale de memorie

Rawnew/deletevă expune la trei categorii de erori:

1. Scurgere de memorie — se alocă cunew, dar nu se apelează niciodată ladelete. Memoria nu este niciodată returnată la sistemul de operare (până la ieșirea procesului). Într-un server cu funcționare lungă, acesta consumă în cele din urmă toată memoria RAM disponibilă.

2. Double Free — apelareadeletepe același indicator de două ori. Al doileadeletecorupă structurile interne de date ale alocătorului de heap, ducând adesea la blocări mult mai târziu în codul care nu are legătură. Acest lucru este extrem de greu de depanat.

3. Use-After-Free — accesarea memoriei dupădelete. Este posibil ca memoria să fi fost reutilizată pentru o alocare diferită, astfel încât ați putea citi sau scrie datele unui alt obiect. Aceasta este o vulnerabilitate de securitate (bun de clasă CVE).

Capcană

Luați în considerare:f(new A(), new B()). Compilatorul poate evalua argumentele în orice ordine și le poate intercala:

1. Alocați memorie pentru A
2. Alocați memorie pentru B
3. Construcția A
4. Construcția B

Dacă pasul 4 este aruncat, A este scurs - nimeni nu sunădeletepe el. Chiar șif(std::shared_ptr(new A()), std::shared_ptr(new B()))a avut această problemă înainte de C++17 (ceea ce garantează că fiecare argument este pe deplin evaluat înainte de următorul). Remedierea: folosiți întotdeaunastd::make_uniquesaustd::make_shared - efectuează alocarea și construcția ca o singură operație, sigură pentru excepții.