C++ Memory Management & RAII
RAII — Resource Acquisition Is Initialization
// RAII in action — file closes automatically
{
std::ofstream file("output.txt");
file << "data" << std::endl;
} // file destructor runs here — file is closed
// Without RAII — manual cleanup, error-prone
FILE* f = fopen("output.txt", "w");
if (!f) return;
fprintf(f, "data\n");
fclose(f); // easy to forget, especially with early returns
RAII binds resource management to object lifetime. When the object goes out of scope, its destructor releases the resource. This eliminates leaks and simplifies error handling.
unique_ptr
#include <memory>
// Create
auto ptr = std::make_unique<int>(42);
std::cout << *ptr; // 42
// Transfer ownership (move, not copy)
auto ptr2 = std::move(ptr);
// ptr is now nullptr
// Custom deleter
auto file = std::unique_ptr<FILE, decltype(&fclose)>(
fopen("data.txt", "r"), fclose
);
// In containers
std::vector<std::unique_ptr<Device>> devices;
devices.push_back(std::make_unique<Switch>("sw01"));
unique_ptr has zero overhead — same cost as a raw pointer. Cannot be copied, only moved. Use it as your default smart pointer.
shared_ptr
auto p1 = std::make_shared<Host>("sw01", "10.50.1.10");
auto p2 = p1; // reference count: 2
std::cout << p1.use_count(); // 2
p1.reset(); // p1 releases its share
std::cout << p2.use_count(); // 1
// object destroyed when last shared_ptr goes away
shared_ptr has overhead: reference count (atomic increment/decrement), control block allocation. Use it only when ownership is genuinely shared.
weak_ptr
auto shared = std::make_shared<int>(42);
std::weak_ptr<int> weak = shared;
if (auto locked = weak.lock()) {
std::cout << *locked; // 42 — temporarily promoted to shared_ptr
}
shared.reset();
if (weak.expired()) {
std::cout << "object is gone";
}
weak_ptr does not extend the object’s lifetime. Use it for caches, observers, and breaking circular references.
Stack vs Heap
// Stack — automatic, fast, fixed size
int x = 42; // stack
std::array<int, 100> arr; // stack
Host h("sw01", "10.50.1.10", 22); // stack (but String members are on heap)
// Heap — manual/smart pointer, flexible size
auto ptr = std::make_unique<Host>("sw01", "10.50.1.10", 22); // heap
auto vec = std::vector<int>(1000); // vector header on stack, elements on heap
Stack allocation is essentially free (just a pointer bump). Heap allocation involves malloc and potential fragmentation. Prefer stack when size is known at compile time.
Move Semantics
std::string a = "hello";
std::string b = std::move(a);
// a is now in a "valid but unspecified state" (likely empty)
// b owns the string data — no copy happened
// std::move in function parameters
void process(std::string data) {
// data is owned here
}
std::string input = "large data...";
process(std::move(input)); // move, not copy
// input is empty
std::move does not move anything — it casts to an rvalue reference, enabling the move constructor/assignment. After a move, the source is valid but its contents are unspecified.
Modern C++ Guidelines
// Prefer: value semantics (stack allocation)
Host h("sw01", "10.50.1.10", 22);
// When you need heap: unique_ptr
auto h = std::make_unique<Host>("sw01", "10.50.1.10", 22);
// When you need shared ownership: shared_ptr
auto h = std::make_shared<Host>("sw01", "10.50.1.10", 22);
// NEVER in modern C++:
// int* p = new int(42); // raw new
// delete p; // raw delete
Rule of thumb: value types on the stack, unique_ptr for single ownership on the heap, shared_ptr only when multiple owners genuinely exist. Raw new/delete should not appear in modern C++ code.