CS 70

Destruction for Custom Classes: Destructors

  • LHS Cow speaking

    So far, we've managed to allocate space all over the heap!

  • RHS Cow speaking

    But remember the rule: Any time you allocate space with new, you should deallocate it with delete!

An object's destructor is called when the object is destroyed. Since destruction will take the names of the object and all of its data members out of scope, it's our last chance to clean up any mess that object has made in memory!

Like constructors, destructors can be synthesized by the compiler. If we don't specify what should happen in a destructor, the compiler's synthesized one will go through and destroy each of the object's data members in turn.

Our data members are all primitive types (pointers are a primitive type), and their destructors will "do nothing" to their values. As a result, the synthesized destructor will end up doing nothing at all for our class. Which means we're going to end up with a memory problem…

What kind of memory error(s) will we get if we use the synthesized destructor for our Barn class?

Writing a Custom Destructor

  • LHS Cow speaking

    So we have an array full of Cow*s on the heap, and each Cow*points to a Cow, also on the heap.

  • RHS Cow speaking

    And we're going to have to delete all of that.

  • Cat speaking

    That sounds familiar…. We wanted to do something similar with Elephants in Week 5 Lesson 2?

  • LHS Cow speaking

    Yup! And here we are doing it! We're going to have two big categories of things that we need to destroy and deallocate:

    • The individual Cows on the heap
    • The array of Cow*s on the heap

Which memory should we destroy and deallocate first?

Here's the start of a Barn destructor:

1| Barn::~Barn() {
2|     // Destroy and deallocate the individual Cows on the heap
3|     for (size_t i = 0; i < cowCount_; ++i) {
4|         _______;
5|     }
6|
7|     // Destroy and deallocate the array of Cow pointers
8|     _______;
9| }

Hint: Remember that we need to give delete the address of the memory that needs to be destroyed and deallocated. So delete wants a pointer of some kind! But it also needs to be a pointer that originally came out of new.

What should go in the blank on line 4?

What should go in the blank on line 8?

Our destructor for the Barn class now looks like

Barn::~Barn() {
    // Destroy and deallocate the individual Cows on the heap
    for (size_t i = 0; i < cowCount_; ++i) {
        delete residentCows_[i];
    }

    // Destroy and deallocate the array of Cow pointers
    delete[] residentCows_;
}

You can check out a runnable version of this code with a working default constructor and destructor using the button below:

Alternatively, you can write the destructor using the start/past-the-end pointer approach (that we saw in Week 5 Lesson 1):

Barn::~Barn() {
    // Destroy and deallocate the individual Cows on the heap
    for (Cow** p = residentCows_; p < residentCows_ + cowCount_; ++p) {
        delete *p;
    }

    // Destroy and deallocate the array of Cow pointers
    delete[] residentCows_;
}

This latter version is shown in the video below.

  • RHS Cow speaking

    Phew! We've done a lot. Let's head up to the parent page and see what we've learned, and maybe take a break.

(When logged in, completion status appears here.)