Manual new/delete has exactly the same dangers as C's malloc/free — memory leaks and dangling pointers — but C++ offers a language-level pattern, RAII, that largely eliminates the need to manage memory by hand at all.
class FileHandle {
private:
std::FILE *file;
public:
FileHandle(const char *path) {
file = std::fopen(path, "r"); // resource acquired in the constructor
}
~FileHandle() {
if (file) std::fclose(file); // resource released in the destructor
}
};
void process() {
FileHandle f("data.txt"); // file opens here
// ... use f ...
} // f's destructor runs automatically here — file is closed, guaranteed,
// even if an exception is thrown somewhere in the middle of process()
RAII (Resource Acquisition Is Initialization) ties a resource's lifetime directly to an object's lifetime: acquire the resource in the constructor, release it in the destructor. Because C++ guarantees destructors run automatically when an object goes out of scope — for any reason, including an exception unwinding the stack — RAII objects can never "forget" to release their resource the way a raw malloc/fopen call paired with a manual free/fclose sometimes does when an early return or an exception skips past the cleanup code.
This single idiom is why modern C++ code rarely calls new/delete, fopen/fclose, or lock/unlock directly — instead, it wraps each resource in a small RAII class (or, more often, reaches for one the standard library already provides): smart pointers for heap memory (next lesson), std::ifstream/std::ofstream for files (previous chapter), and std::lock_guard for mutexes (Concurrency lesson) are all RAII wrappers around exactly this pattern.