C++ Memory Management: new/delete, Stack vs Heap, and RAII
이 글의 핵심
Practical guide to C++ memory: stack/heap, new/delete, RAII, and patterns used every day in production code.
Stack vs heap
Stack memory
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
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
// 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
// Allocate
int* arr = new int[100];
// Use
arr[0] = 1;
arr[99] = 100;
// Free
delete[] arr; // must use [] for arrays!
Class objects
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
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
#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
#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
#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:
// ❌ 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:
// ❌ 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:
// ❌ 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.
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.
Related reading (internal links)
- Memory leaks in practice
- Stack vs heap basics
- Stack vs heap deep dive
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?
Keywords (search)
C++, memory management, new, delete, RAII, heap, stack, smart pointers
Related posts
- C++ series
- Adapter pattern
- ADL
- Aggregate initialization