[2026] C++ Memory Management: new/delete, Stack vs Heap, and RAII

[2026] C++ Memory Management: new/delete, Stack vs Heap, and RAII

이 글의 핵심

Deep dive into C++ memory: stack vs heap, dynamic allocation with new/delete, RAII, smart pointers, and common pitfalls—new, delete[], placement new, and leak patterns.

Stack vs heap

Stack memory

다음은 간단한 cpp 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

void func() {
    int x = 10;      // stack
    int arr[100];    // stack
}  // automatic cleanup

Characteristics:

  • Fast
  • Limited size (often ~1–8 MB per thread)
  • Automatic lifetime

Heap memory

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

void func() {
    int* ptr = new int(10);  // heap
    // ....use ...
    delete ptr;  // manual release
}

Characteristics:

  • Slower allocation
  • Much larger practical limit
  • Manual management (or smart pointers)

Dynamic allocation (new/delete)

Single object

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Allocate
int* ptr = new int;           // default-initialized (indeterminate for int)
int* ptr2 = new int(10);      // value-initialize to 10
int* ptr3 = new int{10};      // C++11 braced init
// Free
delete ptr;
delete ptr2;
delete ptr3;

Arrays

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Allocate
int* arr = new int[100];
// Use
arr[0] = 1;
arr[99] = 100;
// Free
delete[] arr;  // must use [] for arrays!

Class objects

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Person {
public:
    string name;
    Person(string n) : name(n) {
        cout << name << " constructed" << endl;
    }
    ~Person() {
        cout << name << " destroyed" << endl;
    }
};
int main() {
    Person* p = new Person("Alice");
    p->name = "Bob";
    delete p;  // destructor runs
}

RAII (Resource Acquisition Is Initialization)

Basic idea

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class FileHandler {
private:
    FILE* file;
    
public:
    FileHandler(const char* filename) {
        file = fopen(filename, "w");
        if (!file) {
            throw runtime_error("failed to open file");
        }
        cout << "file opened" << endl;
    }
    
    ~FileHandler() {
        if (file) {
            fclose(file);
            cout << "file closed" << endl;
        }
    }
    
    void write(const char* data) {
        fprintf(file, "%s\n", data);
    }
};
int main() {
    try {
        FileHandler fh("output.txt");
        fh.write("Hello");
        // Even if an exception is thrown, destructor closes the file
    } catch (exception& e) {
        cerr << e.what() << endl;
    }
}

Practical examples

Example 1: Avoiding leaks

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <memory>
using namespace std;
// ❌ Leak risk
void badExample() {
    int* data = new int[1000];
    
    if (someCondition) {
        return;  // no delete — leak!
    }
    
    delete[] data;
}
// ✅ RAII-safe
void goodExample() {
    unique_ptr<int[]> data = make_unique<int[]>(1000);
    
    if (someCondition) {
        return;  // automatic cleanup
    }
    
    // automatic cleanup at scope end
}
int main() {
    goodExample();
    return 0;
}

Note: Smart pointers ensure memory is released on exceptions and early returns.

Example 2: Custom memory pool

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <vector>
using namespace std;
template <typename T>
class MemoryPool {
private:
    vector<T*> pool;
    size_t nextIndex;
    
public:
    MemoryPool(size_t size) : nextIndex(0) {
        pool.reserve(size);
        for (size_t i = 0; i < size; i++) {
            pool.push_back(new T());
        }
        cout << "pre-allocated " << size << " objects" << endl;
    }
    
    ~MemoryPool() {
        for (T* obj : pool) {
            delete obj;
        }
        cout << "memory pool destroyed" << endl;
    }
    
    T* acquire() {
        if (nextIndex < pool.size()) {
            return pool[nextIndex++];
        }
        return nullptr;
    }
    
    void reset() {
        nextIndex = 0;
    }
};
int main() {
    MemoryPool<int> pool(100);
    
    int* p1 = pool.acquire();
    int* p2 = pool.acquire();
    
    *p1 = 10;
    *p2 = 20;
    
    pool.reset();  // reuse
    
    return 0;
}

Note: Pools reduce repeated alloc/free cost in hot paths.

Example 3: placement new

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <iostream>
#include <new>
using namespace std;
class Object {
public:
    int value;
    Object(int v) : value(v) {
        cout << "Object(" << value << ") constructed" << endl;
    }
    ~Object() {
        cout << "Object(" << value << ") destroyed" << endl;
    }
};
int main() {
    // Pre-allocated buffer
    char buffer[sizeof(Object) * 3];
    
    // Construct in place
    Object* obj1 = new (&buffer[0]) Object(1);
    Object* obj2 = new (&buffer[sizeof(Object)]) Object(2);
    
    cout << obj1->value << ", " << obj2->value << endl;
    
    // Explicit destructor calls
    obj1->~Object();
    obj2->~Object();
    
    // buffer is stack memory — no delete on buffer
    
    return 0;
}

Note: placement new constructs an object in existing storage; you must call destructors manually.

Common problems

Problem 1: Double delete

Symptom: Crash Cause: Deleting the same pointer twice Fix: 아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ double delete
int* ptr = new int(10);
delete ptr;
delete ptr;  // crash!
// ✅ set to nullptr after delete
int* ptr = new int(10);
delete ptr;
ptr = nullptr;
delete ptr;  // safe (delete on nullptr is a no-op)

Problem 2: delete vs delete[]

Symptom: Leak or heap corruption Cause: Using delete on array allocation Fix: 아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ wrong
int* arr = new int[10];
delete arr;  // wrong — only first element “freed” incorrectly
// ✅ correct
int* arr = new int[10];
delete[] arr;
// ✅ single object
int* ptr = new int(10);
delete ptr;

Problem 3: Dangling pointer

Symptom: Use-after-free Cause: Using pointer after delete Fix: 다음은 cpp를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ dangling
int* ptr = new int(10);
delete ptr;
cout << *ptr << endl;  // undefined!
// ✅ nullptr check
int* ptr = new int(10);
delete ptr;
ptr = nullptr;
if (ptr) {
    cout << *ptr << endl;
} else {
    cout << "pointer is nullptr" << endl;
}

FAQ

Q1: When stack vs heap?

A:

  • Stack: small, short-lived data
  • Heap: large buffers, dynamic size, or long lifetime

Q2: What if new fails?

A: bad_alloc is thrown. 아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

try {
    int* huge = new int[1000000000000];
} catch (bad_alloc& e) {
    cout << "allocation failed: " << e.what() << endl;
}

Q3: malloc vs new?

A:

  • malloc: C API, no constructors
  • new: C++ style, constructors, type-safe

Q4: Why RAII?

A: Exception safety and no manual resource leaks—core to modern C++.

Q5: How do I find leaks?

A:

  • Valgrind (Linux)
  • Visual Studio memory profiler
  • AddressSanitizer (-fsanitize=address)

Q6: Should I always use smart pointers?

A: Prefer them whenever you express ownership. Raw pointers for non-owning observers, C APIs, or legacy interop.

Practical tips

Debugging

  • Check compiler warnings first.
  • Reproduce with a minimal test.

Performance

  • Don’t optimize without profiling.
  • Set measurable goals.

Code review

  • Anticipate common review comments.
  • Follow team conventions.

Practical checklist

Before coding

  • Is this the right technique?
  • Can the team maintain it?
  • Does it meet performance needs?

While coding

  • Warnings cleared?
  • Edge cases handled?
  • Error handling in place?

At review

  • Intent clear?
  • Tests enough?
  • Docs adequate?

C++, memory management, new, delete, RAII, heap, stack, smart pointers

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3