3

C++ delete vector, objects, free memory states that in order to release allocated heap memory after clearing an std::vector, one can do:

vector<int>().swap(myVector);

That makes sense, but I wonder whether the following wouldn't achieve the same. Can someone tell me whether there's a difference?

myVector = vector<int>();
Mat
  • 202,337
  • 40
  • 393
  • 406
matthias_buehlmann
  • 4,641
  • 6
  • 34
  • 76
  • 1
    The second option can call a move assignment, the only way to take advantage of move assignment is to free all the existing memory at the end of the statement. legalities aside, it would be a disgrace if one needs to do the swap, to achieve what the assignment should do. – alfC Dec 23 '21 at 19:20
  • @user1095108 i agree. i think there is no absolute fool proof way to make the capacity of a vector to go to zero. i think there could implementations that always work with a minimum size buffer. you have to rely on common sense of the implementation that an empty vector didn’t even try to allocate. – alfC Dec 23 '21 at 19:27
  • The only fool proof way is to let the vector go out of scope. Why keep it around when you apparently don't intend to reuse its capacity? So drop it, and create a new (guaranteed empty) vector whenever you need one again. – BoP Dec 23 '21 at 19:58
  • @BoP it might be a member variable – matthias_buehlmann Dec 23 '21 at 20:06

3 Answers3

3

In accord with the standard, assignment from a rvalue (ie: move assignment) for (non-std::array) containers is not allowed to invoke the move constructor or assignment operators of T unless the allocator for that container does not propagate on container move-assignment. This means that the only valid implementation for vectors of such allocators is to destroy the current allocation and adopt the allocation in the rvalue. std::allocator does propagate on container move-assignment, so any move assignment for such containers will force adoption of the rvalue's content.

Does this mean that myVector = vector<int>(); is guaranteed to adopt the empty allocation? Not necessarily. It is entirely valid for an implementation to detect that the rvalue container is empty, destroy its own elements, but preserve its internal allocation for later.

By contrast, it is not viable for an implementation of vector::swap to do the same thing. The iterator/pointer/reference preservation requirements ensure that iterators/pointers/references to objects in one container are mapped to those in the other (so long as the allocators permit this through propagation on container swap). This means that the allocations within the two objects must actually be swapped, even if one of them has no allocation. Assignment has no such iterator preservation requirements.

That being said, assuming that an implementation will be pathological and actively try to avoid your attempts to remove a container's allocation is not really a good idea. Similarly, a call to shrink_to_fit cannot guarantee that the capacity will change... but an implementation that didn't would be a really stupid one.

So one might argue that the best code would be the one that cleanly explains what's happening. swap is an idiom that you have to get someone to explain why you're using it on a prvalue like that. clear() followed by shrink_to_fit makes it clear what you're doing just from the documentation; no explanation is needed.

So maybe consider doing just that.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
0

There's no significant difference between those alternatives.

However, there is a third option which I recommend due to it being more obvious to the reader:

myVector.clear();
myVector.shrink_to_fit();
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • but this isn't guaranteed to release the memory, no? – matthias_buehlmann Dec 23 '21 at 18:55
  • @matthias_buehlmann Not any more than the other alternatives, no. – eerorika Dec 23 '21 at 18:55
  • In my understanding the other alternatives should guarantee the release of the memory, since the old object is destroyed. no? – matthias_buehlmann Dec 23 '21 at 18:56
  • @matthias_buehlmann The object remains undestroyed in all these alternatives. While the swap may guarantee that the old allocation is freed, it doesn't guarantee that the default constructor of vector won't make another allocation. – eerorika Dec 23 '21 at 18:57
  • well in the swap case it turns the previous vector into an R value which gets destroyed the moment that R value goes out of scope, no? – matthias_buehlmann Dec 23 '21 at 18:59
  • @eerorika while the default constructor of a temporary empty vector might make an allocation, it's unlikely to be a *significant* allocation. – Mark Ransom Dec 23 '21 at 19:09
  • 2
    @MarkRansom If you are willing to rely on such assumption (and I wouldn't blame you if you did), then why not also rely on the assumption that `shrink_to_fit` on an empty vector will likely deallocate any significant allocation? – eerorika Dec 23 '21 at 19:11
-2

Can someone tell me whether there's a difference?

In case of myVector = vector<int>();

The old memory isn't necessarily deallocated(released).

But in case of vector<int>().swap(myVector);

old memory deallocation will happen.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • Thank you. Can you maybe give some details on why the memory is deallocated in one case but not in the other? – matthias_buehlmann Dec 23 '21 at 19:08
  • @matthias_buehlmann the standard does not mandate that decreasing the size of a vector will decrease the allocation. Keeping the original allocation might be more efficient, especially in the case where the vector grows again. – Mark Ransom Dec 23 '21 at 19:11