A resource leak occurs when acquired capacity is not released, leaving it allocated or unavailable for reuse. In C++, exceptions and immediate termination expose this mistake: destructors may not run, so database connections, sockets, files, or dynamic memory remain held. section 5.9 section 10.4
Recognize the main failure patterns:
- Calling exit bypasses normal scope cleanup, preventing local Connection destructors from returning connections to the pool. section 5.9
- Throwing from a constructor after allocating memory can leak that allocation because the incomplete object’s destructor does not run. section 10.4
- Using RAII requires clear ownership; transferring a pointer to a managing object while the creator still manages it creates ownership confusion. section 10.5
- Returning heap pointers may avoid dangling locals but shifts deletion responsibility to callers and can create further leaks. section 11.4
Termination versus unwinding
With Connection c("payroll") in scope, exit(-1) ends the program before c’s destructor can release the database resource. Replacing exit with an exception and catching at the outer boundary allows stack unwinding to destroy c. For constructor failures, release manually before throwing, or arrange exclusive ownership so cleanup remains automatic. section 5.9 section 10.4 section 10.5
Listing 5.20 Terminating a program with exit
class Connection {
private:
int conn;
public:
Connection(const std::string& name) : conn(0) {
conn = 1; // assume: database connection resource is returned
}
~Connection() {
if (conn)
conn = 0; // assume: destroys database connection resource
std::cerr << "Connection destroyed\n";
}
};
struct Employee {
bool isTerminated() { return true; }
double computePay() { return 42.0; }
};
int main() {
Connection c("payroll");
Employee emp;
if (emp.isTerminated())
exit(-1); #1
std::cout << emp.computePay() << '\n';
return 0;
}
Inspect the terminating example and identify the resource whose destructor is skipped. section 5.9
Listing 5.21 Terminating a program with an exception
class Connection {
private:
int conn;
public:
Connection(const std::string& name) : conn(0) {
conn = 1; // assume: database connection resource is allocated
}
~Connection() {
if (conn)
conn = 0; // assume: returns database connection to pool
std::cerr << "Connection destroyed\n";
}
};
struct Employee {
bool isTerminated() { return true; }
double computePay() { return 42.0; }
};
struct TerminatedEmployee {
std::string message;
TerminatedEmployee(const std::string& msg) : message(msg) {}
};
int main() {
try {
Connection c("payroll");
Employee emp;
if (emp.isTerminated())
throw TerminatedEmployee("Employee was terminated");
std::cout << emp.computePay() << '\n';
} catch (...) {
throw; #1
}
return 0;
}
Compare exception-based termination with exit and follow the cleanup path during unwinding. section 5.9


