12.9 — Destructors

A destructor is another special kind of class member function that is executed when an object of that class is destroyed. Whereas constructors are designed to initialize a class, destructors are designed to help clean up.

When an object goes out of scope normally, or a dynamically allocated object is explicitly deleted using the delete keyword, the class destructor is automatically called (if it exists) to do any necessary clean up before the object is removed from memory. For simple classes (those that just initialize the values of normal member variables), a destructor is not needed because C++ will automatically clean up the memory for you.

However, if your class object is holding any resources (e.g. dynamic memory, or a file or database handle), or if you need to do any kind of maintenance before the object is destroyed, the destructor is the perfect place to do so, as it is typically the last thing to happen before the object is destroyed.

Destructor naming

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.

Generally you should not call a destructor explicitly (as it will be called automatically when the object is destroyed), since there are rarely cases where you’d want to clean up an object more than once. However, destructors may safely call other member functions since the object isn’t destroyed until after the destructor executes.

A destructor example

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


If you compile the above example and get the following error:

error: 'class IntArray' has pointer data members [-Werror=effc++]|
error:   but does not override 'IntArray(const IntArray&)' [-Werror=effc++]|
error:   or 'operator=(const IntArray&)' [-Werror=effc++]|

Then you can either remove the “-Weffc++” flag from your compile settings for this example, or you can add the following two lines to the class:

We’ll discuss what these do in 13.14 -- Converting constructors, explicit, and delete

This program produces the result:

The value of element 5 is: 6

On the first line, we instantiate a new IntArray class object called ar, and pass in a length of 10. This calls the constructor, which dynamically allocates memory for the array member. We must use dynamic allocation here because we do not know at compile time what the length of the array is (the caller decides that).

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

A reminder

In lesson 10.23 -- An introduction to std::vector, we note that parenthesis based initialization should be used when initializing an array/container/list class with a length (as opposed to a list of elements). For this reason, we initialize IntArray using IntArray ar ( 10 );.

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:

This program produces the following result:

Constructing Simple 1
Constructing Simple 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 simple was not destroyed until the end of main().

Global variables are constructed before main() and destroyed after main().


RAII (Resource Acquisition Is Initialization) is a programming technique whereby resource use is tied to the lifetime of objects with automatic duration (e.g. non-dynamically allocated objects). In C++, RAII is implemented via classes with constructors and destructors. A resource (such as memory, a file or database handle, etc…) is typically acquired in the object’s constructor (though it can be acquired after the object is created if that makes sense). That resource can then be used while the object is alive. The resource is released in the destructor, when the object is destroyed. The primary advantage of RAII is that it helps prevent resource leaks (e.g. memory not being deallocated) as all resource-holding objects are cleaned up automatically.

The IntArray class at the top of this lesson is an example of a class that implements RAII -- allocation in the constructor, deallocation in the destructor. std::string and std::vector are examples of classes in the standard library that follow RAII -- dynamic memory is acquired on initialization, and cleaned up automatically on destruction.

A warning about the exit() function

Note that if you use the exit() function, your program will terminate and no destructors will be called. Be wary if you’re relying on your destructors to do necessary cleanup work (e.g. write something to a log file or database before exiting).


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 easier to use.

12.10 -- The hidden “this” pointer
12.8 -- Overlapping and delegating constructors

215 comments to 12.9 — Destructors

  • Foo

    Any reason why direct initialization was used here?:

    • Alex

      Nope, it's been updated now.

      • nascardriver

        If I wrote this code, I used direct-initialization intentionally, because `IntArray` is a good candidate for a list-constructor. Adding a list-constructor now would break the code.
        Using list-initialization here is a bad habit that readers need to drop when list-constructors are introduced. Better not get into that habit in the first place.

        • Alex

          I forgot we covered this point in the prior lesson on std::vector. Given that, I agree with you. Change reverted, and a reminder added into the lesson on why direct initialization was used. Thanks!

  • Hello nascardriver and Alex. Can you pls tell me what topics are you gyz going to cover in chapter 12 , 14 and 15. I really need to know about them. Thank you.

    • Alex

      Going to add chapters on:
      * Type conversion and intro to templates
      * Compound types
      * Intro to using class types: arrays, vectors, and strings
      * Dynamic memory allocation
      * Copy and move semantics

      This will mostly be material from the current chapters reorganized and updated, with some topical gaps filled in.

  • Deva_B.D.M.

    Hi, alex and nascardriver
    do constructors and destructors called when declaring pointers. I tried by myself but didn't call any functions.
    I'm lost right now.
    is it my IDE configuration mistake or behaviors of pointers???

    and thank you for this tutorial which makes the impossible possible!!!!

  • dunno214

    There is an incorrect lesson number in the tip

  • Jerry

    In the destructor, why do only include

    Shouldn't you delete all the member variables? Or do the brackets,[], cover that. Also, I understand that the class creates an array, but how do you know to use brackets? I know the brackets indicate an array, but does that mean that brackets indicate a pointer, like the one [/code] int *m_array{}; [/code ] and brackets also indicate range so it knows that it delete the size?

  • yeokaiwei

    1. Feedback on delete
    Hi Nascar,
    Based on Youtube videos by Bjarne Stroustrup, I think he mentioned that code with "delete" is prone to memory leaks. When there is a "delete", it implies there is a "new".

    Is it a good idea to use "delete" in the tutorial?

  • M Bratbo

    I made this small program to show the difference when a varaiable is a normal local variable an a pointer to dynamically allocated memory. Run the program without command line paramaters and the variable "first" is a normal local variable. it is run with the command line parameter p the variable is a pointer to dynamically allocated memory. Output commands have been added to the constructors and destructors, to show they have been called. When the variable is a local variable the destructor is call automatically, when it is dynamically allocaded, the destructor is called automatically, which means the programmer has to ensure it is done:

  • objects holding resources should not be dynamically allocated

    >>Under the RAII paradigm, objects holding resources should not be dynamically allocated.
    Does it mean that we should avoid using command 'new' for an object of a class (even if its data members are normal type and not holding any resources)? OR should we avoid using command 'new' for classes whose data members are holding any resources (e.g. dynamic memory, or a file or database handle)?which one is correct?

    • nascardriver

      That sentence was wrong, I removed it.
      If you forget to `delete` an object, you've got a problem anyway. It doesn't matter if that object dynamically allocated its members or not.

  • assert

    Shouldn't we include '

    ' in the snippet below?

  • Rushhahb

    >>When an object goes out of scope normally, or a dynamically allocated object is explicitly deleted using the delete keyword, the class destructor is automatically called (if it exists) to do any necessary clean up before the object is removed from memory.

    So, does this mean that the memory address allocated to the object will be deallocated by the operating system?

    • nascardriver

      Memory addresses aren't allocated or deallocated, they're just addresses. You also don't create or remove line numbers.
      If you use `delete`, the object gets deallocated.

  • fensox

    Hey there hope all is well. This line got me. I've read it ten times and can't process..

    Rule: If your class dynamically allocates memory, use the RAII paradigm, and don’t allocate objects of your class dynamically

    Is this saying to me that if, within my class, I have a dynamic array let's say, then in my main program that creates objects of said class I don't want to do it with the "new" keyword? I feel like that can't be what it is saying.. all classes needing dynamic memory shouldn't have to be on the stack should they? Thank's for the help in advance.

    • nascardriver

      Yes, that's what the rule meant. However, it didn't make sense. Whether the class is allocating member dynamically or not, you're leaking memory if you don't delete it. I removed the rule.

      • fensox

        Thank you. Just to be clear, I can have dynamic class members and do dynamic instantiations of that class as long as I am diligent about cleaning up everything? Thanks again.

        • nascardriver

          Correct. `new` and `delete` should be avoided altogether in favor of standard containers and smart pointers. The lessons about smart pointers are currently too far back in the lessons.

  • Mohammad

    What's the symbol pSimple->getID in the last example of this section ? I didn't understand so would you please refer me a lesson that focuses on this?Or you could just give me a short explanation.Sorry to disturb you but advanced thanks.

  • Agna

    In the Tip, when you say: """Then you can either remove the “-Weffc++” flag from your compile settings for this example""", don't you mean the -Werror=effc++ flag?

  • Constant_n

    Hi. I got a warning here about implicit conversions.

    Does the type of the size of dynamic arrays has to be <unsigned long> on every system?

    • nascardriver

      It has to be convertible to `std::size_t`, which is some unsigned integer type. The conversion from a signed integer (`length`), to an unsigned one causes a warning. I added a cast to silence the warning.

  • hellmet

    A small suggestion, I think it would be great to mention that explicitly not calling delete on line 30 will not free up the resources in Simple(2). The pointer goes out of scope and deleted, but the data is still on the heap. I understand this was already covered in the 'Stack and Heap' and later chapters, but perhaps this detail might slip newbies (like me). Or add it as a quiz question!

    • nascardriver

      Thanks for the suggestion!
      I added a comment stating that we have to delete it. I don't think a re-explanation of what happens when you don't delete it is necessary.

  • Anthony

    I'm having a problem with some C++ code, which necessarily uses on the C sockets API, so my first question is whether it's ok to use


    with the buffers and structures associated with sockets in general. I'm pretty sure the answer is yes to this one.

    Secondly, I believe the problem is to do with the way destruction is taking place. Let's say that

    contains (as a member variable on the stack) a pointer to

    . What happens to this pointer when Class A goes out of scope? Does Class A destruct (along with the pointer), leaving Class B intact?

    Thanks :)

    • > it's ok to use [new]

      > it's ok to use [delete]
      To delete objects that were created with `new`, yes. You just can't `delete` objects that were created with `malloc`, but you shouldn't have to anyway.

      > Secondly [...]
      If `A` is only storing a pointer to `B`, then nothing will happen to the `B` object `A` was pointing to (Unless `A`'s user-defined destructor does something to `B`).
      If `A` created the `B`, then `A` should `delete` the `B`.
      If `A` only stores the pointer, but the `B` is owned by something else (ie. something else created it), then you don't have to do anything, the `B` will stay intact.

        • Anthony

          Quick follow-up as I'm still trying to see where I've gone wrong:

          If I have a pointer pointing at some dynamically allocated memory, and then I copy the base address to another pointer (with a different name, obviously, but of the same type), can I use this pointer to deallocate the memory?

          • Yes. Keep in mind that you're deleting the pointed-to memory, not the pointer. Only use `delete` on one of the pointers.

            • Anthony

              Thanks. I've squashed the bug. That one aged me. Mind, they all do.

              It brings up something else. I was dealing with a @struct msghdr from the C sockets API. It has a member @msg_name of type @void*, which contains a socket address (@sockaddr_in, @sockaddr_in6, etc) depending on how you cast it. I've written an init function which uses @calloc() to fill out all the pointer members of the @msghdr struct and will do a @new version of this shortly. If I do this to allocate:

              Would I do the following to deallocate - i.e. I have to cast?

              More generally, when using the C sockets API in C++, is it just a matter of writing C++ wrappers for the parts of it one needs, because that's what I'm doing?

              • > uses @calloc() to fill out all the pointer members
                IIRC you don't have to allocate them, instead point them to regular variables. Check the documentation or tutorials to make sure you're doing it right. I could very well be wrong, this isn't something I've done often.
                Any reason why you're allocating an array with 1 element? That seems odd to me.

                > Would I do the following to deallocate - i.e. I have to cast?
                No, you don't have to cast.

                > is it just a matter of writing C++ wrappers
                It depends on how close to the api you have to be. Most likely, you don't care which sockets api your program is using, so you'd write an abstraction that makes using the sockets very easy, ie. the user (of your code) won't know which sockets api you're using and they don't have to know how it works.
                In the unlikely case that you need to use the socket's function all over the place, you can wrap the individual functions (maybe small groups of functions) so that you don't have to worry about memory management and such and wrap everything in a class so that you don't have to pass the socket's handle around.

                C++23 might add networking capabilities, but that's a long way to go.
                There are many networking libraries for C++. Unless you're doing networking for fun, those libraries should be used (I can't recommend any, I always did networking for fun :)).

                • Anthony

                  I had to take a few days to reflect on your advice. The trickiest task has been to decide whether to allocate 'fairly complicated' socket-related structures like @sockaddr_storage, @addrinfo and @msghdr on the stack or the heap, and how to do so when it comes to the heap. I see that they're not like C++ classes because they don't have overloaded assignment operators, so creating copies (and it turns out I really do need to do so) isn't as straightforward as it could be.

                  I'm pretty sure I have it down, but there is one thing I don't know how to do, which is to do with void pointers. So, if I take the @msghdr structure as an example ( It has a member called @void *msg_control, but in practice, it points to an array of @struct cmsghdr's. Accordingly, I'm trying to allocate memory and perform the copy like this:

                  However, the compiler complains that I'm performing arithmetic on a void pointer, which of course I am through my use of array index operators. My question is, how do I use casting to solve this problem?

                  • You don't need to copy the data at all, unless you want to store a copy of course, but I don't see why you would do that.

                    `msg_control` is a `void*`. As your compiler says, you can't do arithmetic with it (Array indexing is arithmetic).
                    If you're sure that `msg_control` is a `cmsghdr` array, you can cast it. You're using C-style casts. They allow unsafe casts. Use C++ casts.

                    Please see "Ancillary data should only be accessed by the macros defined in cmsg(3)." on the page you linked. Your code might be unsafe.
                    The page also says that `msg_controllen` is the length of the buffer. You're using it as the array's length (ie. number of elements). It might just be worded poorly in the doc.
                    My code assumes that your code is correct. I didn't check to documentation.

                    • Anthony

                      Yes, I'm not certain either about the poorly worded documentation. I think it doesn't really make sense for `msg_controllen` to be the entire length of all the ancilliary data, because the length of each `cmsghdr` buffer is given individually by the `msg_len` member. But then.. I'm not sure. Stack Overflow beckons.

                      Yes, I'm using the macros to access the ancilliary data. But now you make me think. If I make a byte for byte copy, shouldn't they still work? Still, I shall try, aware that it may all be for nothing.

                      The reason I want to store a copy is that I have a Server class that can make many multiple connections with clients, and I want to keep track of not only the socket file descriptor for each of them, but the remote sockaddr and the last msghdr struct received from or sent to the client, too. Maybe a poor design decision, but I'm learning a lot.

                      Thank you for your help, as always.

                  • > I'm not certain either about the poorly worded documentation
                    Look at the value of `msg_controllen` when you receive a message. That should tell you what it is.

                    > shouldn't they [macros] still work?
                    It might even work entirely without them, but if the documentation tells you to use those macros, then you use those macros. It might work without them today or in the current scenario, but the developers of the library are free the make your macro-less code break in an update. This is just like undefined behavior. It might work, it might not, better do it the right way.

                    > I want to store a copy
                    I suggest you to get rid of the C data as soon as possible unless you process it right away. If you use the C data, you'll have to worry about memory, which can be difficult as you've already noticed.
                    You can use valgrind's memcheck to see if you're leaking memory, which can happen quite easily.

                    • Anthony

                      I have tried a stack-allocated 'msghdr' struct too, as a member variable. I'm wondering about it, though. If I have a struct like this, i.e. one that contains member pointers, I can of course allocate memory for these on the stack as well. However, I then have a bunch of member variables, when I really only wanted one (the original 'msghdr' struct), and things get rather messy. How would I do this and keep everything tidy?

    • I'm replying here, because there are too many messages.

      Don't mix regular members and pointer members, that will make your code ugly and harder to use. Since you want to use the original struct and that struct has pointer members, do everything with pointers.

  • Parsa

    A pointer to a class will point to the first initialized member variable? Or how does it work.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]