Jason
Jason

Reputation: 13986

C++ Memory Deallocation

I'm trying to test my understanding of C++ memory allocation.

For the following program:

{
    int a=0;
}

Since a is allocated off the stack it should be freed when the variable goes out of scope, right?

Okay, easy enough. What about this case:

{
    Matrix m(50, 20);
}

Let's say there's a matrix class and I'm creating a new one with 50 rows and 20 columns. Obviously not all of the memory can be allocated off the stack because 50 and 20 could be populated at run time. So I'm guessing that somewhere in the constructor, they allocate memory off the heap.

When that goes out of scope the destructor on m is called? And that desctructor should deallocate (delete) the memory it allocated?

Now it really gets hard:

{
    Matrix t;
    {
        Matrix m(50, 20);
        t=m;
    }
}

What happens then? Does t get assigned to the memory location of m? or does it do a copy of the data in m? If t is a reference to m, then what happens when m goes out of scope? Does the destructor on m get called? or does it wait until t goes out of scope to call the destructor of t/m?

Upvotes: 3

Views: 397

Answers (5)

deworde
deworde

Reputation: 2799

You're correct with the int. When the allocation is called, an int size amount of space is taken up on the stack, and when the variable goes out of scope, that int is "popped off" the stack and the memory is free to be used again.

With the Matrix, you're not 100% right. Just because the data is allocated at run-time, doesn't mean it can't be allocated on the stack, as long as there's room on the stack to put it. What happens here is that when you create the matrix, it is pushed onto the stack. The amount of space it takes up on the stack is dependent on how it's managed. Within its constructor, it may allocate memory to the heap, or it may simply take up a big ol' chunk of stack space. If there isn't room on the stack, you'll get a stack overflow exception at runtime.

You are correct though that if it does allocate memory at construction, it should clean it up at destruction. But it doesn't have to, and in some cases it shouldn't (e.g. if it doesn't actually "own" that memory anymore because it's given/sharing it with another object).

In your final case, what happens is this:

Matrix t;

This creates a Matrix using the default constructor of the class. Whether it reserves any memory here is dependent on what that constructor has in it. It might reserve memory, but it might not.

Matrix m(50, 20);

This creates a Matrix using a different constructor of the class. Again, it might reserve memory, but it might not (e.g. it might not take up any space other than what's on the stack till some "real" data is added to it)

t=m;

What happens here is, again, up to the class. At this point, this is not a constructor, but the assignment function of t:

Matrix& operator=(const Matrix& m){
    ....
}

being called. Some assignment constructors will copy the data, some will copy pointers to the data but not the actual data itself. However, in the latter case, the class should handle the condition where m goes out of scope, by refusing to delete the data, and instead relying on the constructor of t to do it. But again, they don't have to, and may do it badly (especially as controlling this gets complicated)

The critical take from this is that once you reach the level of class creation & destruction, how memory is handled is dependent on the implementation of the class.

Upvotes: 0

NPE
NPE

Reputation: 500893

When that goes out of scope the destructor on m is called? And that desctructor should deallocate (delete) the memory it allocated?

Yes and generally yes.

Now it really gets hard:

{
    Matrix t;
    {
        Matrix m(50, 20);
        t=m;
     }
}

What happens then? Does t get assigned to the memory location of m? or does it do a copy of the data in m?

What happens is that the assignment operator is called:

t.operator=(m);

It is up to you, the implementer of Matrix, to ensure valid semantics. There are several possible approaches:

  1. The assignment operator could make a copy of m's data. In this case there are no difficulties with lifetime and ownership. However, in this approach the assignment is costly.
  2. The assignment operator could make t point to the same data as m. This may be viable, but will require a lot of care to make sure the lifetime of the data is managed correctly, and that modifying one matrix doesn't unexpectedly modify the other. One way to do this is by keeping a reference-counted pointer to the data, and using copy-on-write when modifying data. Some older implementations of std::string are of this type.

Upvotes: 6

Arpit
Arpit

Reputation: 12797

What happens then? Does t get assigned to the memory location of m?

It's a call to copy constructor which do member wise copy by default.

but if you have overloaded an assignment operator then you can either assign the reference or create a complete new object its up to you.

for rest of the Ques:

call to destructor depends on you either you call it explicitly or leave it to default. in default it will be called when the variable or object is out of scope.

Upvotes: 0

Sepand
Sepand

Reputation: 166

C++ memory allocation is really simple, so is object scope. C++ class design is not as simple.

In your examples, each time you close a brace, the local objects, a or m or t (for the outer set of braces in your last example), go out of scope and their destructor is called. In the case of the int, the destructor is simple and deletes the object on the stack. For m, it's a custom destructor for the class Matrix and for any respectable library, you can assume it deallocates the object correctly.

What complicates t is not the destructor, but the assignment from m. In most implementations of matrix classes, t = m would result in the contents of m being copied into t, which would have its own memory on the heap. Then as each object goes out of scope, their corresponding memory is deallocated.

If the implementation is more complex, then its up to the class designer to make sure it handles the destruction of each object correctly (and you to use the library correctly so that it can).

Upvotes: 0

Fred Larson
Fred Larson

Reputation: 62113

Actually, it's pretty easy.

In the first case, you're right. Automatic allocation, automatic deallocation.

In the second case, it's not really any different. The Matrix class constructor handles any additional memory it needs, and its destructor should deallocate it.

In the third class, the inner scope variable is copied to the outer scope variable. The Matrix class should be following the Rule of Three, so copies should be handled correctly.

All of this is assuming a proper implementation of Matrix.

Upvotes: 1

Related Questions