-1

Can anyone provide a proof that one of the following approaches provide a guarantee to free the vector's memory in a platform independent manner?

vector<double> vec;
//populating vec here

Cleaning up:

1- shrink to fit approach

vec.clear(); 
vec.shrink_to_fit();

2- swap approach

vector<double>().swap(vec);
Theo
  • 189
  • 1
  • 1
  • 10
NULL
  • 759
  • 9
  • 18
  • How did you check that neither of these released the memory? – Borgleader Aug 20 '17 at 15:50
  • @Borgleader I did not! If you read the comments in the other question you'll see that a lot of members mention that there is no guarantee. – NULL Aug 20 '17 at 15:52
  • @Borgleader I think the issue is the guarantee, which is of course implementation dependent though right now I'm not sure where you'd find an implementation that doesn't. – Robinson Aug 20 '17 at 15:56
  • @Robinson How is the swap method implementation dependent? – Borgleader Aug 20 '17 at 15:57
  • 1
    Why do you think the swap solution would not free memory? –  Aug 20 '17 at 15:57
  • I'm not sure I understand correctly, but isn't `{ std::vector tmp; std::swap(vec, tmp); }` a guarantee to free `vec`? – Passer By Aug 20 '17 at 15:59
  • @NeilButterworth I don't know. I used it all the time. But in the linked question (releasing memory of struct of vectors) it's been mentioned none provide a guarantee! – NULL Aug 20 '17 at 16:00
  • @NULL I think it was more that swap is considered a "trick", and is implementation dependent, so it's not a guarantee. Though I can't see how. – Robinson Aug 20 '17 at 16:01
  • @Robinson "I think it was more that swap is considered a "trick", and is implementation dependent," - how so? –  Aug 20 '17 at 16:03
  • @PasserBy, the reason that the std::swap() approach you mentioned frees vec is because you change the control of its lifetime by substituting it with tmp (ie, vec -> tmp and vice versa, and the contents of the old vec gets destroyed went tmp goes out of scope). – Timo Geusch Aug 20 '17 at 16:04
  • 2
    It is a dupe - if you want to empty a a vector, use the swap idiom, which uses standard, documented features of the language. –  Aug 20 '17 at 16:07
  • @TimoGeusch Isn't that the intended effect? – Passer By Aug 20 '17 at 16:07
  • @PasserBy, you are correct, that is indeed the intended effect - the OP's post didn't make it clear to me if the question was about this particular idiom or just a "regular" swap between two vectors. There's nothing stopping you from swapping the contents of two vectors and then keep using both. – Timo Geusch Aug 20 '17 at 16:11
  • 1
    C++ talks about deallocating memory. Deallocated memory is under no obligation to be "freed", it just becomes invalid. `shrink_to_fit` very explicitly refuses to provide a guarantee of deallocation (it's a **non-binding** request). `swap` doesn't guarantee it either. Destruction of the temporary object will *probably* deallocate its memory, but no promises again (C++ standard library dtors are not guaranteed to deallocate anything or otherwise be leak-free). We depend on the implementation to do the right thing, in this and many other cases. – n. m. could be an AI Aug 20 '17 at 16:24
  • 1
    TimoGeusch answers in comments below. The misunderstanding here is that an empty vector allocates no memory. This isn't true. It isn't specified in the standard so it could be 0 or 4 or 256 or whatever. It's implementation specific. When we say "free memory", we really mean to return it to its brand new, constructed state. – Robinson Aug 20 '17 at 16:26
  • @Robinson To clarify, once and for all, the swap idiom returns the vector to the state it had when it was first created. That is the best you can do with Standard C++. –  Aug 20 '17 at 16:27
  • 1
    @Robinson: *"One would expect a new vector to at least allocate some memory, perhaps a trivial amount."* -- No, one would not expect that. I am, in fact, not aware of any implementation that does do that. – Benjamin Lindley Aug 20 '17 at 16:29
  • @BenjaminLindley It's not specified in the standard. That's the point. – Robinson Aug 20 '17 at 16:30
  • @Robinson: The point of what? Your statement was about what would be expected by an implementation, not what is specified by the standard. – Benjamin Lindley Aug 20 '17 at 16:32
  • 1
    @BenjaminLindley I just stepped through std::vector default construction in Visual Studio 2017. It calls: _Ptr = ::operator new(_User_size); with _User_size of 16. – Robinson Aug 20 '17 at 16:35
  • 1
    Additionally, shrink_to_fit does call _Tidy, which releases the memory entirely (in Visual Studio 2017). – Robinson Aug 20 '17 at 16:52
  • 3
    @Robinson: Yes. But it only does this for debugging purposes. And the memory it is allocating is not used for the purpose of storing elements of the vector. It is used for tracking iterators. Note what happens in the sections surrounded by `#if _ITERATOR_DEBUG_LEVEL == 0`. No allocations. – Benjamin Lindley Aug 20 '17 at 17:00

1 Answers1

2

Creating a vector with using new is unlikely to do what I think you'd want to do either.

std::vector implementations usually only "guarantee" that they will allocate enough memory to hold the requested number of elements at minimum. The latter is important because asking the OS or runtime for more memory when you need to grow the vector is an expensive operation that may potentially trigger an element copy as well. For that reason, a lot of the implementations use some heuristics to determine how big the allocation is going to be when the vector has to grow to a certain size. For example, one implementation I'm familiar with doubles the size of the allocation every time new memory is required, giving you a 2^x allocation schema.

With an allocation schema like that, trying to shrink a vector from, say, 90 to 70 elements is pretty much guaranteed to keep the allocated memory size the same to reserve for additional room for growth.

If you need exact memory allocation sizes for whatever reason, you'll pretty much either have to use std::array if you know the sizes at compile time, or manage an array yourself.

Timo Geusch
  • 24,095
  • 5
  • 52
  • 70
  • IIRC a lot of vector implementations use a different growth factor – Passer By Aug 20 '17 at 16:06
  • @PasserBy, that's definitely implementation dependent and can be dependent on which runtime allocator it expects to use as well. That's why there is no defined memory allocation algorithm for vectors in the standard and it's left to the implementor, with only the minimum requires spec'd. – Timo Geusch Aug 20 '17 at 16:09
  • So what you're suggesting is that any given new vector isn't guaranteed to be using some small (trivial) amount of memory even if its size is zero, so the "swap trick" doesn't guarantee size() == 0 && amount_of_memory_reserved == 0. At least that is not specified in the standard. – Robinson Aug 20 '17 at 16:12
  • @Robinson, correct. I would expect a brand new vector with 0 elements to have some memory allocated (mainly so the first push doesn't trigger an immediate (re)-allocation. Doesn't mean that there aren't implementations out there that do reserve zero payload memory for an empty vector. – Timo Geusch Aug 20 '17 at 16:15
  • 2
    @Timo "Doesn't mean that there aren't implementations out there that do reserve zero payload memory for an empty vector." - that is in fact what most implementations do. This is part of the C++ philosophy - you don't pay for what you don't use. –  Aug 20 '17 at 16:19
  • @NeilButterworth Visual Studio 2017 appears to have a default allocation of 16 (64 bit) or 8 (32 bit). – Robinson Aug 20 '17 at 16:42
  • 1
    @Robinson Hmm, GCC gives a capacity of zero, which is what I would expect a good implementation to do. But as has we have observed here several times, this isn't standardised.. –  Aug 20 '17 at 16:49