Classes, invariants, and RAII
Construction, encapsulation and deterministic cleanup
A class is useful when every public operation must preserve a relationship among values. Constructors establish the invariant before an object becomes usable; methods preserve it. A public data struct is appropriate for an unconstrained snapshot, while mutable service state generally belongs behind methods.
class PositiveLimit {
Int value_;
public:
explicit PositiveLimit(Int value):value_(value) {
if(value<=0) throw std::invalid_argument("limit");
}
Int value() const {return value_;}
};Member initialization follows declaration order, not the order written in the initializer list. Initialize members directly rather than default-constructing and assigning later. explicit prevents an unintended implicit conversion from an integer into a domain object.
RAII ties resource release to an owner's destructor. Stack unwinding destroys fully constructed local objects when an exception exits a scope. A lock_guard releases its mutex on both normal return and exception; a vector destroys its elements and frees its allocation automatically. Destructors should not throw during cleanup, because a second escaping exception during unwinding terminates the process.
std::mutex mutex;
{
std::lock_guard lock(mutex); // released at closing brace, including exception exit
}Model a bounded stock invariant as 0 <= used <= capacity. A failed reservation must not change used. Avoid overflow by checking requested against capacity-used. Releasing more than currently reserved is invalid and leaves state unchanged. The final practice uses a local reservation guard so returning from any scope restores stock. Its owner must outlive the guard; a borrowed reference does not provide lifetime extension. This makes ownership of the reservation explicit without implementing manual try/catch cleanup at every call site.
Put it into practice
Implement the exercise in a full-screen workspace, then run its tests.