The problem
A resource is anything a program borrows and must give back: heap memory from new, an open file, a lock. Each must be returned exactly once. Forget, and it leaks. Return it twice, and the program can crash.
Without RAII you pair every new with a delete by hand. In this version an early return skips the delete, so the four ints are never given back: a memory leak. Every extra way out is another chance to forget.
RAII at work
RAII (Resource Acquisition Is Initialization) ties a resource to an object. Making a Buf gets the memory: line 2 runs new int[4]{}. Its destructor, ~Buf on line 3, gives it back with delete[].
Line 6 makes b, a local object in work's stack frame. Its constructor runs at once and asks the heap for four ints, all 0. Now b.p holds their address, and b owns them.
Line 7 writes 42 into the first int through b.p. You use the memory as usual. Because b owns it, you never write delete inside work.
Here stop is true, so line 8 returns early and line 9 never runs. In the manual version this exit leaked the memory. Now watch what C++ does with b as work ends.
When b goes out of scope, C++ calls its destructor automatically. delete[] p gives the four ints back to the heap, then b's place on the stack is gone. No leak, and you wrote no delete in work.
Everywhere
An exception leaves work too. C++ unwinds the stack: it leaves each scope and runs the destructors of the objects in it. So b is destroyed and the memory still goes back. Every exit runs ~Buf exactly once.
The standard library is built on RAII: std::vector and std::unique_ptr own memory, std::fstream a file, std::lock_guard a lock. Prefer them to raw new and delete. Copying a Buf would free twice: copy rules fix that.
What happens
;; step 1/9 · resource resource how you get it how you give it back heap memory new / new[] delete / delete[] a file std::fopen std::fclose a lock m.lock() m.unlock() forget to give back → a leak: it stays taken until the program ends give back twice → a double free: behaviour not defined, often a crash
Line 2 is a default member initializer: Buf has no constructor written out, so the compiler makes a default constructor that runs it. Writing Buf() : p(new int[4]{}) {} does the same. Stack unwinding runs destructors when an exception leaves a scope and a matching catch exists. If no catch is found, std::terminate is called and it is up to the implementation whether the stack is unwound first.