Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop destructor from calling when returning stack variable

Tags:

c++

destructor

I have a class List which allocates memory automatically to store a list of items.

It has a destructor which deallocates this memory:

List::~List()
{
    free(memory);
}

This means, if I create a new list, I can use delete to call the destructor and free the memory.

The destructor will also be called once the variable is out of scope which is ALMOST always what I want. e.g:

int func()
{
    List list;
    list.push(...);
    ...
    return 47;
}

However, what if I want to return that list?

List func()
{
    List list;
    return list;
}

I am alright with the list being copied because it's returned by value, and doesn't have much data to copy (only a few ints, and a pointer).

However, the memory that the list allocated and has a pointer to, contains a LOT of data.

Since I am returning the list, the list is being copied along with the pointer to this data.

Since the list is now out of scope, the destructor is called, which frees up the pointer to that data, even though the copy also has the pointer.

How do I prevent this destructor from being called?


1) There is probably a solution by creating a copy constructor, however, I don't want to do this because then all the data at that pointer will likely have to be copied which is a waste of time and temporarily requires double the memory to be allocated.

2) I know I could just create a pointer List* list and return that, but I want to avoid the necessity of allocating new memory for that list if possible, and also want to avoid wasting more memory for a pointer (8 bytes or something).


Thanks in advance,

David.

like image 762
David Callanan Avatar asked Jan 02 '23 23:01

David Callanan


1 Answers

Assuming you’re using C++11 or later, you just create a move constructor which leaves the old list empty.

In order to avoid similar problems, you also need to delete the copy constructor, or actually write it so your class can be copied (don’t worry; in most cases, including the one you were worried about with returning from a function, the compiler will use the move constructor or get rid of the copy/move entirely, especially after C++17).

This is greatly simplified by storing the pointers as unique_ptr, which will help make sure you don’t make a mistake, and will mean you don’t need to explicitly write the copy or move constructors.


If you’re stuck on pre-C++11, you can’t do this, at least not without a small storage-space penalty. You’d need to use a reference-counting pointer like boost::shared_ptr (a version was added to the standard library with C++11, but it sounds like you would rather move-only semantics), which will only free the memory when it’s the last one one left referencing that memory. This makes copying, creating, and destroying lists slightly slower (since it needs to check/update the reference counter), and it takes some space to store the count, but these costs are relatively small compared to those of actually copying the list’s contents.

Note that in this case two copies always point to actually the same list. If you update one “copy”, the other also gets updated. This is usually not the behavior that users of your class would expect in C++.

like image 63
Daniel H Avatar answered Jan 05 '23 14:01

Daniel H