Appearance
RAII, Ownership, and Smart Pointers
RAII—resource acquisition is initialization—binds a resource to an object's lifetime. The resource may be heap memory, a file, a mutex, a socket, a transaction, or any state requiring paired cleanup.
Scope is a cleanup mechanism
cpp
#include <fstream>
#include <string>
bool write_report(const std::string& path) {
std::ofstream output{path};
if (!output) return false;
output << "ready\n";
return static_cast<bool>(output);
} // output closes here on every return path
int main() {
return write_report("report.tmp") ? 0 : 1;
}The stream destructor closes the file on early return and during stack unwinding if a later operation throws. Streams set state flags rather than throwing by default; callers can inspect the state, or explicitly enable exceptions. Destruction should make cleanup unavoidable, while explicit operations report failures that cannot sensibly be handled from a destructor.
unique_ptr is exclusive heap ownership
cpp
auto report = std::make_unique<Report>(configuration);
consume(std::move(report));unique_ptr cannot be copied. Moving transfers ownership and leaves the source empty. Prefer make_unique because it constructs the object and owner together. Often no pointer is needed at all: use a direct local value or store values in a container.
Custom deleters adapt C handles:
cpp
struct FileCloser {
void operator()(std::FILE* file) const noexcept {
if (file) std::fclose(file);
}
};
using File = std::unique_ptr<std::FILE, FileCloser>;shared_ptr represents shared ownership, not general sharing
shared_ptr maintains a reference-counted control block. Copying extends lifetime; destruction decrements the strong count. It solves the specific case where no single owner can determine lifetime.
Costs include allocation/control metadata, atomic reference-count operations in common implementations, less predictable destruction, and the possibility of cycles. Two objects holding shared_ptr to one another never reach zero. Use weak_ptr for a non-owning link that can be tested and temporarily locked.
If architecture has one clear owner and many borrowers, unique_ptr plus references/pointers expresses it better.
Raw pointers and references are useful non-owners
A raw pointer need not mean dangerous ownership. It often means optional borrow; a reference means required borrow. The API should prevent borrowed handles from escaping beyond the owner's lifetime.
Views such as span, string_view, iterators, and ranges are also borrows. Container reallocation or erasure may invalidate them even while the container itself lives.
Locks are resources too
cpp
std::lock_guard lock{mutex};
update_shared_state();The guard releases the mutex at scope exit. Manual lock()/unlock() pairs are vulnerable to early returns and exceptions. RAII converts a temporal rule into an object-lifetime rule visible to the compiler and reviewer.
Optional prompts
Explain: When is shared_ptr justified?
Answer: When lifetime is genuinely co-owned and no single owner can determine it. It should not replace a clear ownership design merely because several functions access the object.
Debug: A view into a vector dangles after push_back. What happened?
Answer: Growth may reallocate the vector's storage, invalidating pointers, references, iterators, and spans into the old allocation.