CS 70

Destruction

Why Do We Need a Destruction Phase?

We've said that in the lifecycle of objects, destruction mirrors construction. But while it's clear that variables need to be initialized before they can be used, why do we need to do anything at all when we're done with them?

The destruction phase is all about cleaning up properly after ourselves. Sometimes, there is no cleanup to do, but sometimes there is.

Suppose, for example, we have a Document class representing a document we read from the disk and are editing. Maybe when an instance of this class goes away, we want to make sure that any changes to that document are written back to the disk rather than thrown away.

  • LHS Cow speaking

    There's an example a bit like this with the Display class in the ASCIImation assignment. The Display class represents the destination for the frames of an animation. Two different implementations are provided, one that uses ncurses to show the animation in a terminal window, and one that writes the frames to a text file. In both cases, when the Display object is destroyed, it needs to make sure that the terminal or file is properly cleaned up.

  • Rabbit speaking

    While the animation is playing, ncurses hides the cursor and does other things to make the animation look good. When the animation is done, we want to make sure that the terminal is put back the way it was before we started.

  • Dog speaking

    And if we're writing to a file, we want to make sure that the file is properly closed so that all the data is written out, right?

  • LHS Cow speaking

    Exactly! And the Display class's destructor is the perfect place to do that. It means all this work happens automatically when the Display object goes away, so the user of the class doesn't have to remember to do it.

Destruction of Primitive Types

When a value of a primitive type (e.g., int, or double, or any pointer type) is destroyed, no clean up is necessary.

C++ could do something like zero-out that part of memory, but on most systems, following C++'s No Overhead principle, destruction typically does nothing for values from primitive types.

Destruction of Instances of Classes

  • RHS Cow speaking

    Destruction for an instance of a class is much more interesting!

  • LHS Cow speaking

    Once again, the author of the class gets to define what should happen!

The function that defines what should happen any time an instance of the class is destroyed is called the destructor.

A destructor's name is the same as the class (and the constructor), but with a ~ in front of it.

  • RHS Cow speaking

    The ~ character is a “tilde”, pronounced like "till-duh". Some coders might also call it a “twiddle” (but for now, at least, no one calls it “tilde tag”).

So the destructor for the Cow class would be named ~Cow().

A Cow object doesn't have anything particular to do to clean up when it is destroyed, but we'll eventually see examples where the destructor has a really important job to do!

  • Horse speaking

    Hayyyy, I remember from math that ~ sometimes means “not”! Is this related?

  • LHS Cow speaking

    That's a great connection! We can think of the destructor as the “not-Cow” function: it unmakes a Cow object!

  • Dog speaking

    If it's like the opposite of a constructor, can I call it the deconstructor?!?

  • LHS Cow speaking

    To avoid confusion, it's best to follow standard terminology. The phase is destruction, and the special member function we implement is the destructor.

  • Alien speaking

    And, canine creature, deconstruction is “a method of critical analysis of philosophical and literary language which emphasizes the internal workings of language and conceptual systems, the relational quality of meaning, and the assumptions implicit in forms of expression”, which is clearly a distinct concept.

  • Jo speaking

    I have a whole group of friends I hang out with at the local coffee shop, and we were into deconstruction before it was cool!

Synthesized Destructors

  • LHS Cow speaking

    Like constructors and assignment operators, the compiler is willing to synthesize a destructor for us.

Destructor Specification

Code Destructor? Who Writes It?
~Cow(); Yes You
~Cow() = default; Yes The compiler

Notes

  • For the default form, the synthesized destructor will destroy each data member. For primitive data members, that means it will “do nothing” (as above). For data members that are themselves instances of classes, it will call the appropriate destructor.
  • Notice there's no delete form—it's important that you have a destructor, whether you write it yourself or you get the compiler to do it for you. (Keep reading…)
  • LHS Cow speaking

    Unlike constructors and assignment operators, it's a (really, really, really) bad idea not to have a destructor at all!

  • Cat speaking

    Ah, I see. So we can't just do, like, ~Cow() = delete;?

  • LHS Cow speaking

    Oh , you can… After all, this is C++! C++ is sure you know what you are doing when you make it impossible to destroy instances of a class.

  • RHS Cow speaking

    As usual, it's up to you to be responsible and not disable the destructor.

Important note! Since the destructor is automatically called whenever an object goes into the destruction phase of its life, you should never, ever, ever (ever) call the destructor directly!

  • Horse speaking

    Whoa, it seems super weird to write a function that we never call.

  • LHS Cow speaking

    Totally, but that's what you have to do with destructors. You have to trust that the compiler will make a call to the destructor at the right time.

  • RHS Cow speaking

    When it's time for the object to be destroyed, the destructor will be called automatically. We promise!

  • Hedgehog speaking

    So why can we call it if we're never supposed to?

  • Bjarne speaking

    It's there for experts. In certain advanced contexts it is useful to have this kind of control.

  • LHS Cow speaking

    But not in CS 70.

Practice Problem

Suppose our Cow class defines the following destructor:

Cow::~Cow() {
    cout << "Cow destructor called" << endl;
}

And that we write the following program using the Cow class:

1| void cowGoesMoo(Cow c) {
2|     c.moo();
3| }
4|
5| int main() {
6|     Cow bessie;
7|     cowGoesMoo(bessie);
8| }

When our program runs, how many times will the message Cow destructor called be printed?

(When logged in, completion status appears here.)