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 VideoWriterclass in the ASCIImation assignment (HW 3).

  • RHS Cow speaking

    You call the release() member function to make it write the contents of the video.

  • LHS Cow speaking

    Until the video file is written, at least some of it is in memory.

  • Goat speaking

    It'd be cool if it wrote itself out automatically when the video writer is destroyed.

  • LHS Cow speaking

    In fact, it does!

  • RHS Cow speaking

    We didn't rely on this functionality, but the destruction phase for VideoWriter objects calls release() as part of its cleanup.

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

  • LHS Cow speaking

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

  • RHS 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. That character is most properly called a “tilde”, but sometimes coders also call it a “twiddle”.

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

    Whoah, 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.)