liveBook · Manning

Double delete error

As explained in 100 C++ Mistakes and How to Avoid Them

A double free is an error in which the same allocated resource is deallocated more than once.

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

How 100 C++ Mistakes and How to Avoid Them covers it

How deep it goes
Explains the failure, ownership cause, RAII correction, and need for normal-path and error-path testing.
Examples
Uses a Grades class, dynamically allocated arrays, destructors, and invalid negative-score exceptions.
What it assumes
Assumes familiarity with pointers, dynamic allocation, destructors, and exceptions while reinforcing RAII ownership rules.
What you can do after
Diagnose/debug with it by tracing ownership and testing exception paths for repeated deletion.
How it connects
Links double deletion to RAII, resource ownership, destructors, exceptions, and resource leaks.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 C++ Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.