Resource management coordinates acquiring, using, and releasing dynamic or limited resources. Memory, files, sockets, and database connections can outlive the variables that refer to them, so ownership and cleanup belong in the class invariant. section 7.6
Treat these as practical failure points:
- Failing to implement the copy constructor, copy assignment operator, and destructor can make copied objects share unique resources, causing incorrect behavior and potentially system-wide effects. section 7.6
- Pair every allocation with deallocation on normal and exceptional paths; an exception thrown during construction can otherwise leak a dynamic array. section 10.5
- Prefer resource-managing classes and RAII so destruction releases resources automatically, then use copy and move operations that preserve the intended ownership. section 10.0 section 10.5
A constructor that leaks on invalid input
A Grades constructor allocates an array and then throws when it encounters a negative score. Because construction stops before the object can clean up its member, the allocated array leaks. A safer design lets the owning object acquire the array and release it through destruction, while validation and average calculation remain separate operations.
Listing 7.7 A copy constructor that shares unique resources
class TextSection {
// assume a clever implementation
};
class Page {
private:
TextSection* headers; #1
TextSection* body;
public:
Page(TextSection* h) : headers(h), body(new TextSection()) {}
Page(const Page& o) : headers(o.headers), body(o.body) {} #2
Page& operator=(const Page&);
};
int main() {
Page p1(new TextSection());
Page p2 = p1;
return 0;
}
Compare a copy constructor that shares a body pointer with one that creates an independent resource, then examine move operations and unique_ptr ownership. section 7.6
Listing 10.7 A resource leak in the presence of an exception
class Grades {
private:
int size;
double* grades;
double average;
public:
Grades(int size, const double* grades) : size(size), grades(0),
average(0) {
if (size == 0)
return;
this->grades = new double[size]; #1
for (int i = 0; i < size; ++i) {
if (grades[i] < 0) #2
throw std::invalid_argument("negative score");
this->grades[i] = grades[i];
}
double sum = 0.0;
for (int i = 0; i < size; ++i)
sum += this->grades[i];
average = sum/size;
}
~Grades() { delete [] grades; }
double getAverage() const { return average; }
};
int main() {
int count;
std::cout << "Enter number of items to average: ";
std::cin >> count;
double* scores = new double[count];
for (int i = 0; i < count; ++i) {
std::cout << "Enter score: ";
std::cin >> scores[i];
}
try {
Grades g(count, scores);
std::cout << "Average score is " << g.getAverage() << '\n';
} catch (const std::invalid_argument& ex) {
std::cout << ex.what() << '\n';
}
delete [] scores;
return 0;
}
Inspect the exceptional path that leaks grades, then compare shared-resource and exclusive-ownership RAII implementations. section 10.5
