C++, End to End
Dynamic Memory Allocation
new and delete are C++'s direct interface to dynamic memory: allocate an object on the heap, and free it when you're done. They're the mechanism underneath every container and smart pointer in the standard library. You should understand them thoroughly and use them directly as rarely as possible.
new and delete
Using plain delete on something allocated with new[], or delete[] on something allocated with plain new, is undefined behavior. The two must always be paired correctly. This mismatch is exactly the kind of easy-to-miss bug that's a strong argument for not writing manual new/delete in the first place.
Why manual new/delete is a last resort now
Every object you allocate with new has to be freed with exactly one matching delete, on every code path, including ones that exit early, throw an exception, or return in the middle of a function. Miss one, and you leak memory. Free the same pointer twice, or use it after freeing it, and you get undefined behavior that might not show up until much later, in a completely unrelated part of the program.
std::vector, std::string, and smart pointers (covered in this course's dedicated move-semantics chapter) all solve exactly this problem using RAII: the object's constructor acquires the resource, and its destructor releases it, and destructors run automatically during stack unwinding, including when an exception is thrown. Wrap the raw resource in one of these, and the leak above becomes structurally impossible instead of something you have to remember to avoid.
Memory leaks and double frees, concretely
Both of these compile without warning or error in general, and neither necessarily crashes immediately. That delay between the mistake and its visible effect is exactly why these bugs are so much harder to track down than a syntax error, and exactly why the RAII-based alternative isn't just "nicer", it removes the entire bug category.
Placement new
Placement new constructs an object at a specific, already-allocated memory address, instead of allocating new memory itself: new (address) Type(args). It's a genuinely advanced, rare tool, used mostly by people implementing their own memory allocators, object pools, or low-level containers (in fact, this is exactly how std::vector itself constructs elements into its pre-allocated storage internally). You're very unlikely to need it in application code; it's mentioned here so the name doesn't come as a surprise if you encounter it, not as something to reach for.
Try it yourself: find and fix the leak›
This function leaks memory on one specific path. Find it, then fix it, first with a manual delete, and then again the better way (removing manual memory management from this function entirely).
int* findAndDouble(int* arr, int len, int target) {
int* result = new int(0);
for (int i = 0; i < len; ++i) {
if (arr[i] == target) {
*result = arr[i] * 2;
return result; // fine, caller is responsible for this one
}
}
return nullptr; // BUG: "result" was already allocated and is now leaked
}