Navigation



8.6 — Destructors

A destructor is another special kind of class member function that is executed when an object of that class is destroyed. They are the counterpart to constructors. When a variable goes out of scope, or a dynamically allocated variable is explicitly deleted using the delete keyword, the class destructor is called (if it exists) to help clean up the class before it is removed from memory. For simple classes, a destructor is not needed because C++ will automatically clean up the memory for you. However, if you have dynamically allocated memory, or if you need to do some kind of maintenance before the class is destroyed (eg. closing a file), the destructor is the perfect place to do so.

Like constructors, destructors have specific naming rules:
1) The destructor must have the same name as the class, preceded by a tilde (~).
2) The destructor can not take arguments.
3) The destructor has no return type.

Note that rule 2 implies that only one destructor may exist per class, as there is no way to overload destructors since they can not be differentiated from each other based on arguments.

Let’s take a look at a simple string class that uses a destructor:

class MyString
{
private:
    char *m_pchString;
    int m_nLength;

public:
    MyString(const char *pchString="")
    {
        // Find the length of the string
        // Plus one character for a terminator
        m_nLength = strlen(pchString) + 1;

        // Allocate a buffer equal to this length
        m_pchString = new char[m_nLength];

        // Copy the parameter into our internal buffer
        strncpy(m_pchString, pchString, m_nLength);

        // Make sure the string is terminated
        m_pchString[m_nLength-1] = '\0';
    }

    ~MyString() // destructor
    {
        // We need to deallocate our buffer
        delete[] m_pchString;

        // Set m_pchString to null just in case
        m_pchString = 0;
    }

    char* GetString() { return m_pchString; }
    int GetLength() { return m_nLength; }
};

Let’s take a look at how this class is used:

int main()
{
    MyString cMyName("Alex");
    std::cout << "My name is: " << cMyName.GetString() << std::endl;
    return 0;
} // cMyName destructor called here!

This program produces the result:

My name is: Alex

On the first line, we instantiate a new MyString class and pass in the C-style string “Alex”. This calls the constructor, which dynamically allocates memory to hold the string being passed in. We must use dynamic allocation here because we do not know in advance how long of a string the user is going to pass in.

At the end of main(), cMyName goes out of scope. This causes the ~MyString() destructor to be called, which deletes the buffer that we allocated in the constructor!

Constructor and destructor timing

As mentioned previously, the constructor is called when an object is created, and the destructor is called when an object is destroyed. In the following example, we use cout statements inside the constructor and destructor to show this:

class Simple
{
private:
    int m_nID;

public:
    Simple(int nID)
    {
        std::cout << "Constructing Simple " << nID<< std::endl;
        m_nID = nID;
    }

    ~Simple()
    {
        std::cout << "Destructing Simple" << m_nID << std::endl;
    }

    int GetID() { return m_nID; }
};

int main()
{
    // Allocate a Simple on the stack
    Simple cSimple(1);
    std::cout << cSimple.GetID() << std::endl;

    // Allocate a Simple dynamically
    Simple *pSimple = new Simple(2);
    std::cout << pSimple->GetID() << std::endl;
    delete pSimple;

    return 0;
} // cSimple goes out of scope here

This program produces the following result:

Constructing Simple 1
1
Constructing Simple 2
2
Destructing Simple 2
Destructing Simple 1

Note that “Simple 1″ is destroyed after “Simple 2″ because we deleted pSimple before the end of the function, whereas cSimple was not destroyed until the end of main().

As you can see, when constructors and destructors are used together, your classes can initialize and clean up after themselves without the programmer having to do any special work! This reduces the probability of making an error, and makes classes easy to use.

8.7 — The hidden this pointer
Index
8.5 — Constructors

42 comments to 8.6 — Destructors

  • sandor

    Hello,
    can I call the destructor explicitly?

  • Sandor, technically you can… but generally you shouldn’t.

    If the object is dynamically allocated, just delete it using the delete keyword, which will call the destructor anyway.

    If the object is not dynamically allocated, calling the destructor explicitly is actually dangerous. Consider what would happen if you explicitly call the destructor of a local variable — the destructor would delete the variable. Then when the local variable went out of scope, the destructor would get called AGAIN. Then bad things happen.

  • sandor

    Ok, but how can I call it?.I thought of having for example several spaceship objects in the main function. Now for some reason one of those spaceships gets “shot down”, so I want to call the destructor to “destroy” it, but I don’t want to wait till the main function ends.

  • Sandor, if you need objects that may be destroyed in the middle of a function, the best way to go is to dynamically allocate them in the first place and then delete them using the delete keyword. You should not need to explicitly call the destructor.

    In the case where you have multiple objects (eg. spaceships), usually the best way to go is an array of pointers. If you need a new spaceship, find the first empty array slot and allocate a new spaceship there. If a spaceship is destroyed, delete it and then set the pointer in the array to NULL. This way, you can have as many spaceships as there are elements in the array. This also gives you the ability to loop through the array to do things to all the spaceships (eg. move them).

  • Jason

    In the above code, shouldn’t:

    delete m_pchString;

    be:

    delete m_pchString[];

    [ Almost. It should be delete[] m_pchString. I've been programming C++ for 10 years and I _still_ forget to use array delete instead of normal delete with arrays. It's one of the things I really dislike about C++. Great catch. -Alex ]

  • Renu

    std::cout << “Destructing Simple” << nID<< endl;
    should be
    std::cout << “Destructing Simple” << m_nID<< endl;

  • colton

    Alex, I want to tell you how much I appreciate all of the time you have spent on this tutorial. I have used some of the other online tutorials, (cplusplus.com, cprogramming.com, etc) but I find myself most always coming back here whenever I dont understand something and need a really thorough, cogent, and easy to understand explanation.

    Thanks!

  • [...] 2007 Prev/Next Posts « 8.4 — Access functions and encapsulation | Home | 8.6 — Destructors » Wednesday, September 5th, 2007 at 3:10 [...]

  • Daniel

    If I have a class named “thing” and I create a static instance of that class named “myThing” like this “static thing myThing”. Is the destructor called on this instance? If so when? I would assume it would at the end of the programme but I really don’t know.

    • Daniel

      Well I just did a test myself and found that it was as I expected called at the end of the programme if anyone else wants to try this them selves this is what I did
      #include <iostream>
      using namespace std;

      class basic
      {
      public:
      basic() {
      cout << "constructor called" << endl;
      }

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

      static basic myThing;

      int main()
      {

      system("pause");
      return 0;
      }
      It outputs:
      constructor called
      pause stuff
      destructor called

      • I find a lot of time when I have questions in C++ I can answer them just by writing a test program like you did.

        The answer is that when you have static variables, they get constructed before main() and destructed just before your program ends (assuming you don’t exit() early)

        • Daniel

          Thanks,

          QUOTE: assuming you don’t exit() early
          ah, is there an easy way around this? to call the destructor on an exit() call?

          • As far as I know, there’s no way to explicitly call the destructor on an exit() call.

            However, instead of using exit() to terminate your program, there are other options. One way is to design your program so it “exits normally” (eg. at the end of main) instead of wherever it happens to be at the moment. A related idea is to throw an exception and catch it in main(), allowing the program to exit normally.

  • std::cout << "Constructing Simple " << nID<< endl;
    and
    std::cout << "Destructing Simple" << m_nID << endl;

    should have std::endl; :)

    Great tutorials btw; already had a basic grasp of C, but really like the ideas and usage of classes in OOP.

    [ Fixed! Thanks -Alex ]

  • [...] 2007 Prev/Next Posts « 8.6 — Destructors | Home | 8.8 — Constructors (Part II) » Thursday, September 6th, 2007 at 10:20 [...]

  • Darren Fuller

    Just wanted to mention that the first example fails to build for me (I’m using GCC 4.3.2) and throws the following error:

    warning: deprecated conversion from string constant to ‘char*’

    If I change the constructor as follows though it works fine:

    MyString(const char *pchString="")
    {
        ...
    }
    

    [ Since we never modify pchString anyway, it should have been made const in the first place. Thanks! -Alex ]

  • M.N. Jayaram

    Hi

    When I run the MyString program it outputs

    My name is: Alex0

    Actually the problem is fixed if the line no. 21 is commented out, since strncpy takes care of null terminating the string.

       //m_pchString[m_nLength-1] = '' 

    Thanks for the great tutorials!

    • Ah yes, somehow I had a ‘\\0′ instead of a ‘\0′.

      Just so you know, strncpy() does _not_ add a ‘\0′ to the end of the destination string. However, it will copy one that already exists in your source string. The problem comes in when your source string is LONGER than your destination buffer. In that case, it will copy the first N characters out of the source string into the destination buffer, and your destination string will end up unterminated. That’s why I put the explicit terminator on the end of the destination.

  • Joe

    Shouldn’t the line where memory for an array of Simple objects is being dynamically allocated read:

    Simple *pSimple = new Simple[2] (using brackets instead of parenthesis)

    And isn’t it redundant to specify the object type at the beginning of the allocation? Couldn’t the line read pSimple = new Simple[2] ?

    I thought that when dynamic memory is being allocated it was understood that the identifier before the = operator is a pointer of the type specified after the word new.

    • If I was trying to allocate an array of Simple, then you would be correct. I was just trying to allocate a single one, passing the constructor the value 2.

      It IS redundant to specify the object type when doing an allocation — however, C++ makes you do it anyway. In the next version of C++, you will be able to use the auto keyword to automatically infer the data type from the assignment.

  • jeremy

    hi alex,

    just want to ask.destructor cannot be overloaded right? but it can be called more than 1 time.
    as soon as each object is destroyed.
    in this code: } // cSimple goes out of scope here

    Was the destructor called twice?

    Please explain. thanks

    • Destructors can not be overloaded. They never take any parameters and never return anything.

      Destructors should not be called more than 1 time, which will happen automatically when the variable goes out of scope, or manually when you delete it. If you try to destruct a variable that has already been destructed, your program will crash.

  • KH

    i want to ask that for MyString(const char *pchString=”")
    why “” are needed? what is the uses?

    • baldo

      The “” simbolyze an empty string. So, the function parameter has a default value of an empty string. For example, you can put “default string” and if the user didn’t input any string when he called the MyString function, the parameter will have the default value of “default string”.

  • In your last example on this lesson, you included the characters “->”. Is this equivalent to the selector operator “.”?

  • manoj

    some where i read that Constructor is a program which is use to check the state of the object.is it right? what is the exact definition of constructor?? what will happen in case of reference object???
    pls explain briefly..???

  • choo

    hello guys… could somebody tell me what’s wrong with my code…

    #include
    using namespace std;

    class CRectangle {
    int *width, *height;
    public:
    CRectangle (int,int);

    int area () {
    return (*width * *height);
    }
    };

    CRectangle::CRectangle (int a, int b) {
    width = new int; //can i remove this?why
    height = new int; //can i remove this?why
    *width = a;
    *height = b;
    }

    int main () {
    CRectangle rect (3,4);
    cout << "rect area: " << rect.area() << endl;

    return 0;
    }

    • D.M. Ryan

      I tried removing those two lines you commented and I got a runtime error with the data you supplied.
      Here’s why: literals aren’t already-declared variables. You need either the

      width = new int; height = new int;

      or else you need to pass the data with variables already declared in main(). Otherwise, the program doesn’t know where to stick the values being passed to the class.

      Replacing (3,4) with (iii, jjj) in main(), and pre-defining the two variables, gets the program running properly.

      • D.M. Ryan

        I made a mistake, which I couldn’t edit away, in my earlier response. You need the dynamically-allocated variable inside the class because the pointers in your class don’t allocate data by themselves. Changing the width and height variables from pointers to regular variables, and renovating the rest of the class to square with that change, makes it work okay without

        width = new int;
        height = new int;
  • D.M. Ryan

    I found nothing wrong with your code when I complied it with Visual Studio 2005 – provided that the “#include” was changed to

    #include<iostream>

    You need the iostream library to tell the compiler what “cout” and “endl” are.

    Other than that, it worked fine for me.

  • SWEngineer

    Simple well explained tutorial.

    Thanks.

  • sanjeev_e

    Hi Alex,

    I think instead of using the below code

    strncpy(m_pchString, pchString, m_nLength); // Make sure the string is terminated

    m_pchString[m_nLength-1] = ”;

    You can use

    memset(m_pchString, 0, m_nLength);
    strncpy(m_pchString, pchString, m_nLength – 1);

    Because m_nLength is strlen(pchString) + 1. “+1″ is for the NULL character here. So, you should use m_nLength-1 in strncpy. Please correct if I’m wrong.

  • nguyenvulong

    m_nLength = strlen(pchString) + 1;
    m_pchString[m_nLength-1] = ”;

    => This one is unnecessary because you don’t need to add 1 to nLength and then decrease it by 1 at the second line. Just keep it original :

    MyString(const char *str = “”)
    {
    string1len = strlen(str) ;
    string1 = new char[string1len] ;
    strncpy( string1, str, string1len ) ;
    // and you may wanna add string1[string1len] = ”
    }

  • Ingegerd

    there are lots of unknown points in this topic for me. For example:
    -what is that “new” when we use it and how it works?
    especially :
    // Allocate a buffer equal to this length
    m_pchString = new char[m_nLength]; ->how it could be possible , how we can write char to an array name?
    -and usage of that “const” keyword at:
    public:
    MyString(const char *pchString=”")
    these points are unclear for me.I hope somebody can explain.thnx

  • Addmagnet

    ” … However, if you have dynamically allocated memory, or if you need to do some kind of maintenance before the class is destroyed (eg. closing a file) … ”

    I thought it was the object that was destroyed, not the class?

You must be logged in to post a comment.