Is a destructor called when an object goes out of scope?
Asked Answered
D

5

49

For example:

int main() {
    Foo *leedle = new Foo();

    return 0;
}

class Foo {
private:
    somePointer* bar;

public:
    Foo();
    ~Foo();
};

Foo::~Foo() {
    delete bar;
}

Would the destructor be implicitly called by the compiler or would there be a memory leak?

I'm new to dynamic memory, so if this isn't a usable test case, I'm sorry.

Designation answered 17/7, 2013 at 14:45 Comment(8)
No, you need to call the delete leedle yourself.Engender
Can you make an answer and elaborate on why?Designation
Yes to the question in the title, No to the question in the body. You have to manually delete everything you new. If you use new in the constructor (provided no exceptions are thrown) you can call delete in the destructor and it'll clean up memory for you.Brocade
Your code isn't even valid. Please read a C++ book - this is a basic question covered many times on SO.Antivenin
Read something about RAII and use smart pointers (shared_ptr, unique_ptr, etc.)Cicily
possible duplicate of Object destruction in C++Antivenin
I don't see how this is a possible duplicate. It has a specific question with a specific test case.Designation
The problem is that the question and the test case don't match.Engender
C
114

Yes, automatic variables will be destroyed at the end of the enclosing code block. But keep reading.

Your question title asks if a destructor will be called when the variable goes out of scope. Presumably what you meant to ask was:

will Foo's destructor be called at the end of main()?

Given the code you provided, the answer to that question is no since the Foo object has dynamic storage duration, as we shall see shortly.

Note here what the automatic variable is:

Foo* leedle = new Foo();

Here, leedle is the automatic variable that will be destroyed. leedle is just a pointer. The thing that leedle points to does not have automatic storage duration, and will not be destroyed. So, if you do this:

void DoIt()
{
  Foo* leedle = new leedle;
}

You leak the memory allocated by new leedle.


You must delete anything that has been allocated with new:

void DoIt()
{
  Foo* leedle = new leedle;
  delete leedle;
}

This is made much simpler and more robust by using smart pointers. In C++03:

void DoIt()
{
  std::auto_ptr <Foo> leedle (new Foo);
}

Or in C++11:

void DoIt()
{
  std::unique_ptr <Foo> leedle = std::make_unique <Foo> ();
}

Smart pointers are used as automatic variables, as above, and when they go out of scope and are destroyed, they automatically (in the destructor) delete the object being pointed to. So in both cases above, there is no memory leak.


Let's try to clear up a bit of language here. In C++, variables have a storage duration. In C++03, there are 3 storage durations:

1: automatic: A variable with automatic storage duration will be destroyed at the end of the enclosing code block.

Consider:

void Foo()
{
  bool b = true;
  {
    int n = 42;
  } // LINE 1
  double d = 3.14;
} // LINE 2

In this example, all variables have automatic storage duration. Both b and d will be destroyed at LINE 2. n will be destroyed at LINE 1.

2: static: A variable with static storage duration will be allocated before the program begins, and destroyed when the program ends.

3: dynamic: A variable with dynamic storage duration will be allocated when you allocate it using dynamic memory allocation functions (eg, new) and will be destroyed when you destroy it using dynamic memory allocation functions (eg, delete).

In my original example above:

void DoIt()
{
  Foo* leedle = new leedle;
}

leedle is a variable with automatic storage duration and will be destroyed at the end brace. The thing that leedle points to has dynamic storage duration and is not destroyed in the code above. You must call delete to deallocate it.

C++11 also adds a fourth storage duration:

4: thread: Variables with thread storage duration are allocated when the thread begins and deallocated when the thread ends.

Chippewa answered 17/7, 2013 at 14:51 Comment(3)
Great answer -- I find this topic addressed very effectively by first pointing out that the [automatic] pointer will be destroyed, before going on to discuss the semantics of the [dynamic] indirect object. With Eric's correction in place, this answer is perfect.Antivenin
@LightnessRacesinOrbit: Take a look, I made yet another edit.Chippewa
More technically accurate though I think you lost the conciseness.Antivenin
K
7

Yes, if an object goes out of scope, the destructor gets called. BUT No, the destructor won't be called in this case, because you only have a pointer in scope, that pointer has no particular destructor, so there will be no indirect call to Foo's destructor.

This example is the application domain of smart pointers like std::unique_ptr and std::shared_ptr. Those are actual classes that, unlike raw pointers have a destructor, (conditionally) calling delete on the pointed-to object.

Btw, Foo's destructor deletes bar, bur bar has never been initialized nor assigned to an address that points to an actual object, so the delete call will give undefined behavior, likely a crash.

Kingsley answered 17/7, 2013 at 14:49 Comment(0)
D
2

there would be a memory leak indeed. The destructor for the object which goes out of scope (the Foo*) gets called, but the one for the pointed-to object (the Foo you allocated) does not.

Technically speaking, since you are in the main, it is not a memory leak, since you up to when the application is not terminated you can access every allocated variable. With this respect, I cite Alexandrescu (from Modern C++, the chapter about singletons)

Memory leaks appear when you allocate accumulating data and lose all references to it. This is not the case here: Nothing is accumulating, and we hold knowledge about the allocated memory until the end of the application. Furthermore, all modern

Of course, this does not imply that you should not call delete, as it would be an extremely bad (and dangerous) practice.

Dolf answered 17/7, 2013 at 14:48 Comment(2)
You could elaborate a bit more.Designation
sorry, I mistakenly submitted while writing itDolf
B
1

First note that the code wouldn't compile; new returns a pointer to an object allocated on the heap. You need:

int main() {
    Foo *leedle = new Foo();
    return 0;
}

Now, since new allocates the object with dynamic storage instead of automatic, it's not going out of scope at the end of the function. Therefore it's not going to get deleted either, and you have leaked memory.

Bayonet answered 17/7, 2013 at 14:50 Comment(4)
Please stop with the outmoded "heap" vs "stack" gubbins.Antivenin
It's an abstraction leak. The language allows you to create objects with automatic, static and dynamic storage duration. Where they end up in physical or virtual memory is neither important nor specified by the language. Repeating "on the stack" and "on the heap" only helps propagate this nightmare...Antivenin
(Yes, @R.MartinhoFernandes... there is thread-local storage, too!)Antivenin
I've edited the answer, does it reflect modern terminology better now?Bayonet
H
-5

In this instance, when main returns it's the end of the program, the Operating system will handle freeing all resources. If, for instance this was any other function, you would have to use delete.

Hellhole answered 17/7, 2013 at 14:49 Comment(5)
True, but you can't just rely on the OS to do all the work for you. ;)Designation
-1 for teaching bad practices to an obvious newbie. Plus the C++ Standard does not guarantee that the OS will reclaim resources, or that OSs even exist.Chippewa
And even then the OS would probably just plainly reclaim the memory with calling the destructor.Liturgy
Os guaranteed will release all memory, however, this is off-topic for the current question.Calumniation
Not always - for example, if an object that held resources on the GPU and the OS just reclaimed the memory, as it does not guarantee that it will call any destructor, or even knows they exist, then a memory leak will occur in vidmem.Anabranch

© 2022 - 2024 — McMap. All rights reserved.