A double delete occurs when code deletes a pointer and later deletes the same pointer again. The result is undefined behavior; the system may crash, especially along an error path. section 10.5
The mistake appears when an RAII object assumes ownership of a resource while the original owner still manages it. Its destructor correctly deletes the resource, but an exception handler or caller deletes it first. section 10.5
Avoid the failure by making ownership explicit:
- Transfer full ownership to the RAII object and never access or delete the resource afterward. section 10.5
- Prefer a constructor that acquires the resource, allowing its destructor to handle normal and exceptional completion consistently. section 10.5
- Test both normal and error paths, because the duplicate deletion may occur only when an exception is thrown. section 10.5
Listing 10.8 RAII with a shared resource
class Grades {
private:
int size;
double* grades;
public:
Grades(int size, double* grades) : size(size),
grades(grades) {} #1
~Grades() { delete [] grades; } #2
double getAverage() const {
if (size == 0)
return 0.0;
double sum = 0.0;
for (int i = 0; i < size; ++i) {
if (grades[i] < 0)
throw std::invalid_argument("negative score");
sum += grades[i];
}
return sum/size;
}
};
int main() {
int count;
std::cout << "Enter number of items to average: ";
std::cin >> count;
double* scores = new double[count]; #3
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; #4
}
return 0;
}
Shows a Grades object receiving an externally allocated array and deleting it, creating the ownership boundary where double deletion can occur. section 10.5
Listing 10.9 RAII with exclusive ownership
class Grades {
private:
int size;
int next;
double* grades;
public:
Grades(int size) : size(size), next(0), grades(0) {
this->grades = new double[size]; #1
}
~Grades() { delete [] grades; } #2
void addGrade(double grade) { grades[next++] = grade; }
double getAverage() const {
if (size == 0)
return 0.0;
double sum = 0.0;
for (int i = 0; i < next; ++i) {
if (grades[i] < 0)
throw std::invalid_argument("negative score");
sum += grades[i];
}
return sum/size;
}
};
int main() {
int count;
std::cout << "Enter number of items to average: ";
std::cin >> count;
Grades g(count); #3
for (int i = 0; i < count; ++i) {
std::cout << "Enter score: ";
double grade;
std::cin >> grade;
g.addGrade(grade);
}
try { #4
std::cout << "Average score is " << g.getAverage() << '\n';
} catch (const std::invalid_argument& ex) {
std::cout << ex.what() << '\n';
}
return 0;
}
Contrasts shared ownership with exclusive ownership by having Grades allocate and exclusively manage its own array. section 10.5
