Freeing memory C ++

I am trying to test my understanding of memory allocation in C ++.

For the following program:

{
    int a=0;
}

      

Since it a

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

Okay, easy enough. How about this case:

{
    Matrix m(50, 20);
}

      

Let's say there is a matrix class and I create a new one with 50 rows and 20 columns. Obviously, not all memory can be allocated from the stack, because 50 and 20 can be filled at runtime. So my guess is that somewhere in the constructor they are allocating memory from the heap.

When this goes out of scope, the destructor is called on m

? And this descriptor should free (delete) the allocated memory?

Now it gets really complicated:

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

      

What's happening? Is t written to memory location m? or does it make a copy of the data in m? If t is a reference to m, then what happens when m is out of bounds? Is the destructor called on m? or does it wait until t is out of scope to call t / m's destructor?

+3


source to share


5 answers


When this goes out of scope, the destructor on m is called? And is this desctructor supposed to free (delete) the allocated memory?

Anyway, yes.

Now it gets really complicated:

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

      

What's happening? Is t written to memory location m? or does it make a copy of the data in m?



It happens that the assignment operator is called:

t.operator=(m);

      

It is up to you, the executor Matrix

, to provide the correct semantics. There are several possible approaches:

  • The assignment operator can make a copy of the data m

    . In this case, there are no difficulties with life expectancy and ownership. However, with this approach, the appointment is expensive.
  • The assignment operator could do t

    point to the same data as m

    . This may be viable, but it requires a lot of care to ensure that the data's lifetime is properly managed and that changing one matrix does not unexpectedly change the other. One way to do this is to keep the pointer counting data references and using copy-on-write when the data changes. Some older versions std::string

    are of this type.
+6


source


Actually, it's pretty easy.

In the first case, you are right. Automatic distribution, automatic release.

In the second case, this is not entirely true. The constructor of the Matrix class handles any additional memory it needs, and its destructor must free it.



In the third class, the inner scope variable is copied to the outer scope variable. The Matrix class must follow rule 3, so copies must be processed correctly.

All this assumes the correct implementation of the Matrix.

+1


source


Memory allocation in C ++ is really simple, so it's a scope of objects. C ++ class design is not as simple.

In your examples, every time you close the parentheses, the local objects a or m or t (for the outer set of curly braces in your last example) go out of scope and their destructor is called. In the case of int, the destructor is simple and removes the object on the stack. For m, this is a custom destructor for the Matrix class, and for any respectable library, you can assume that it releases the object correctly.

What complicates t is not a destructor, but an assignment from m. In most implementations of matrix classes, t = m will cause the contents of m to be copied to t, which will have its own memory on the heap. Then, when each object goes out of scope, their corresponding memory is freed.

If the implementation is more complex, then the class designer must make sure that he destroys each object correctly (and you are using the library correctly so that he can).

0


source


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

      

This is a call to copy the constructor, which uses a member copy by default.

but if you've overloaded the assignment operator, you can either assign the reference or create a complete new object that you want.

for the rest of the Ques:

calling the destructor is up to you, either you call it explicitly, or you leave it at the default. by default it is called when a variable or object goes out of scope.

0


source


You are correct with int. When allocation is called, an int is taken on the stack, and when the variable goes out of scope, that int pops the stack and the memory is free to use again.

With a matrix, you are not 100% right. Just because the data is allocated at runtime does not mean that it cannot be allocated on the stack if there is room on the stack. What happens here is that when the matrix is ​​created, it is pushed onto the stack. The amount of space it takes up on the stack depends on how it is managed. Inside its constructor, it can allocate memory on the heap, or it can just take a large chunk of stack space. If there is no space on the stack, you will get an exception at runtime.

You are correct, although if it allocates memory on build, it should clear it on destruction. But this is not necessary, and in some cases it should not (for example, if he no longer "owns" this memory, since he gave / passed it to another object).

In your last case, the following happens:

Matrix t;

      

This creates a matrix using the default constructor . Whether it retains any memory here depends on what that constructor has in it. It may reserve memory, but it may not.

Matrix m(50, 20);

      

This creates a matrix using another class constructor. Again, it might reserve memory, but it might not (for example, it might not take up any space other than what's on the stack until some "real" data is added to it)

t=m;

      

What's going on here, again, down to class. At the moment, this is not a constructor, but an assignment function t:

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

      

... Some assignment constructors will copy data, some will copy pointers to the data, but not the actual data itself. However, in the latter case, the class must handle the condition when m goes out of scope by refusing to delete data and instead relying on t's constructor to do so. But then again, they shouldn't and can do it badly (especially as it gets hard to control)

The critical choice from this is that once you get to the level of creating and destroying a class, how memory is handled depends on the implementation of the class.

0


source







All Articles