"Right" way to deallocate an std::vector object
Asked Answered
B

12

49

The first solution is:

std::vector<int> *vec = new std::vector<int>;
assert(vec != NULL);
// ...
delete vec;

An alternative is:

std::vector<int> v;
//...
vec.clear();
vec.swap(std::vector<int>(vec));

The second solution's a bit of a trick --- what's the "right" way to do it?

Update:

I'm aware that the destructor will be called once it's off the stack, I was curious about other methods.

Boomer answered 16/6, 2010 at 15:8 Comment(11)
Why assert(vec != NULL)? new will not return NULL.Pueblo
Some older implementations (or compilers without exceptions!) doElstan
The effect of the code in the "trick" second solution is to reset the vector's internal capacity to 0, which cannot be done otherwise. It doesn't deallocate the vector.Bigoted
Also note that in many compilers when you're compiling in release mode asserts are removed. So you'd never want to depend on any code in the assert to take care of an error condition that could occur in the deployed system.Rearm
The second (and the only right) variant certainly doesn't need the vec.clear();.Warthman
@sbi: as written it does; it's using the "copy and swap" trick that preserves the data and deallocates any extra capacity. To deallocate all the memory, vec = std::vector<int>(); will suffice.Husserl
@Jacob: Perhaps it would be good to edit your first solution to use the nothrow form of new, i.e. std::vector<int> *vec = new (nothrow) std::vector<int>;, or simply drop the assert(). That would address the comments @James and @Martin made.Quaky
@Mike: I thought the standard idiom was vec.swap(std::vector<int>())? That wouldn't need a clear().Warthman
@James McNellis: assert(vec != NULL) is a way to tell the reader "I know that new never returns null pointer". The main purpose of assertions is to serve as formalized comments, not to verify something that might or might not happen. If the author used an if (vec != NULL) then your remark would be justified. But in case of an assert it is completely off. This assert is absolutely correct and absolutely appropriate use of an assertion. This is exactly what assertions are for.Waddington
@AndreyT: While I don't disagree, I think it's at least odd to test (or document) basic implementation functionality like that. Should one assert(vec.empty()) after calling vec.clear() as well to tell the reader "I know that the previous line emptied the vector?"Pueblo
@James McNellis: Each case should be treated individually. assert(vec.empty()) immediately after calling vec.clear() is excessive in my opinion. But knowing about the popular misconceptions about new (and broken implementations), this assertion appears to be more than appropriate to me.Waddington
D
58

The simplest and most reliable way to deallocate a vector is to declare it on the stack and simply do nothing.

void Foo() {
  std::vector<int> v;
  ...
}

C++ guarantees that the destructor of v will be called when the method executes. The destructor of std::vector will ensure any memory it allocated is freed. As long as the T type of the vector<T> has proper C++ deallocation semantics all will be well.

Dummy answered 16/6, 2010 at 15:11 Comment(5)
@Jacob: Then why are you trying to do anything else? There's no need, nor is it ideomatic, to try to do anything to deallocate a vector.Garnettgarnette
@Billy: You're right, I can't think of a situation where I'd need anything else. I rewrote the code so the huge vector was used and dropped in a separate functionBoomer
Note you can only do this with std::vector because although the object is allocated on the stack the data is actually on the heap. std:: just handles the new/delete for you.Elstan
You don't necessarily need to move the code that uses the vector to another function. If you surround the code block where it's used with { ... }, the destructor will be called at the end of that block.Vanmeter
If your control needs to be more complex than just the code blocks, but you still want RAII, you can use a boost::optional or a boost::variant with one type, or std::aligned_storage_t and call the destructor manually at convenience. Alternatively you can use a std::unique_ptr but you loose the stack allocation, you fragment memory, lock the global malloc mutex, and lose perf.Anthem
H
22

The simplest way to deallocate all the storage in a vector, without destroying the vector object itself, is

vec = std::vector<int>();

Your second variant will have the same effect, but it jumps through more hoops on the way. The "copy and swap" trick deallocates any extra capacity in the vector, and can be useful if it contains some data you want to keep. If there's no data, then there's no need for copying or swapping.

Husserl answered 16/6, 2010 at 16:45 Comment(4)
is this any different than vec.resize(0)?Selig
... or the clearer vec.clear()? (No pun intended)Malm
Yes, it's different because resize and clear both do the same thing - they clear the contents of the vector (calling the member destructors, etc), but does NOT deallocate the memory already reserved by the vector. i.e. you're changing the size() of the vector, but not the capacity(). Creating a new vector and assigning it to the old one DOES deallocate the original vector's memory.Flippant
This style I think has the benefit of readability over the swap method that is shown. JMOHexapartite
A
14
std::vector<int> vi;
/*push lots of stuff into the vector*/

// clean it up in C++03
// no need to clear() first
std::vector<int>().swap(vi);

// clean it up in C++0x
// not a one liner, but much more idiomatic
vi.clear();
vi.shrink_to_fit();
Alainealair answered 16/6, 2010 at 15:23 Comment(4)
shrink_to_fit is just a hint, actual shrinking is not guaranteed.Rolling
The reason shrink_to_fit is a hint it to allow fancy stuff with memory. Shrinking to fit would be meaning less if the vector use something like small string optimization(unlikely), or if vectors always allocated from a pool and the smallest size was larger than your current vector size. It can be assumed however that shrink_to_fit() will make the vector as small as is reasonable.Alainealair
The swap trick doesn't work if the vectors have different allocators! I just discovered this the hard way. Probably quite a rare occurrence so I don't know if it's worth mentioning any more prominently than this comment.Sedum
I think it should be mentioned why does this work. It is related to the copy and swap idiom. Basically with std::vector<int>().swap(vi) you create an rvalue of an empty vector, then you swap it with the original one. The scope of the rvalue is restricted to this statement, so after the swap it immediately goes out of scope and its destructor deallocates the data associated with it.Stercoricolous
S
3

I agree with Mike Seymour try this then you will notice that the last is working fine

const int big_size = 10000;
vector<double> v( big_size );
cout << "Before clearing, the capacity of the vector is "
  << v.capacity() << " and its size is " << v.size();
v.clear();
cout << "\nAfter clearing, the capacity of the vector is "
  << v.capacity() << " and its size is " << v.size();
vector<double>().swap( v );

cout << "\nAfter swapping, the capacity of the vector is "
  << v.capacity() << " and its size is " << v.size();

vector<double> v1( big_size );
v1 = vector<double>();
cout << "\n After vector<double>();, the capacity of the vector is "
  << v1.capacity() << " and its size is " << v1.size();
Sherrilsherrill answered 9/2, 2016 at 20:28 Comment(2)
Can you provide a link to the Mike Seymour reference?Gao
@Gao I think it's one of the answers: https://mcmap.net/q/275473/-quot-right-quot-way-to-deallocate-an-std-vector-objectDepositary
U
2

My guess here is that you have a vector which temporarily contains a large amount of data. Once the vector has been cleared, it will still take up all of this memory. You want to release this memory after you are done with it, but you the function/object you are working with has not finished.

Solutions in decreasing order of desirability:

  1. Rework the code so that the vector using code is in it's own block/function/object so that it will be destroyed naturally
  2. Use the swap trick, this way you don't have to worry about making sure that the vector is deallocated in all circumstances. It's lifetime will be tied to the object/function you are in.
  3. new/delete the vector. This will free up a little bit more memory then the previous method but is also harder to make sure no memory is leaked.

The only technical difference between swapping and deleting is the base vector itself is not destroyed. This is a small overhead and is not worth worrying about (as long as you do eventually destroy the vector)

The larger consideration is which makes it easier to write correct code, and I believe swap wins over deleting there, but is worse then moving the vector somewhere else.

Unstring answered 16/6, 2010 at 15:32 Comment(0)
C
1

Don't use memory allocation functions unless you really need to. If your class needs a vector, always, just ad the std::vector member directly. No need to do memory allocation here.

In the cases where you need the dynamic vector, allocating and deleting it like in your first example is 100% correct.

In the second example, the call to std::swap is strictly spoken not needed, because the clear method will clear the vector, making it empty. One possible problem is that there is no guarantee that the vector will actually free the memory, giving it back to the operating system (or to the run time). The vector might keep the allocated memory just in case you will fill the vector right after clearing it. The call to std::swap may be a trick to 'force' the vector to free its internal data, but there is no guarantee that this will actually happen.

Cordierite answered 16/6, 2010 at 15:17 Comment(0)
E
0

Delete deallocates the memory, the memory is then free for the next object but the vector has gone.

The 2nd trick frees any excess memory but leaves the vector intact, but empty.

Elstan answered 16/6, 2010 at 15:11 Comment(0)
P
0

Although both appear to work, I see no reason not to just call delete on the pointer. The vector should have a destructor that is called that will handle everything else.

Porta answered 16/6, 2010 at 15:12 Comment(0)
K
0

If you just let the vector go out of scope, it will clean itself up appropriately with no extra work. If the vector is a member variable of a class, and you want it to deallocate its contents before its owner is destructed, then just call vec.clear().

If you want to keep the vector but deallocate the memory that holds its contents, then vec.swap(std::vector<int>()); will do that. There's no need to copy-construct the temporary into the original unless vec contains items that you want to keep and you just want to shrink the allocated memory down to something close to the current size().

Kirbykirch answered 16/6, 2010 at 15:13 Comment(0)
A
0

This is not a valid comparison because the examples are dealing with different kinds of objects: dynamic duration and local duration. You can call the destructor OR use the swap trick (aka shrink_to_fit) with either one. The right way depends on whether you need the vector object to persist or not.

For example, you may need it to persist if there are references or pointers to it that must remain valid, in which case shrinking is the only way, regardless of how it was allocated.

Aristocracy answered 16/6, 2010 at 15:28 Comment(0)
B
0

I'm not sure why your second example uses a copy constructor for the temporary rather than a default constructor. That would save you the .clear() line of code.

You could make this generic for any object, even if it's not a container. I'm assuming here that std::swap is specialized to call vector::swap.

template<typename T>
void ResetToDefault(T & value)
{
    std::swap(T(), value);
}

std::vector<int> vec;  
//...  
ResetToDefault(vec);
Brasserie answered 16/6, 2010 at 15:31 Comment(1)
std::swap(T(), value) won't compile in C++03 because T() is an rvalue and cannot be bound to a reference.Rolling
Z
0

In the case that the vector really needs to be on the heap don't forget about:

std::auto_ptr<std::vector<int> > vec(new std::vector<int>);

especially useful with code like:

std::auto_ptr<std::vector<int> > vec(vectorFactoryFunction());
Zachariah answered 16/6, 2010 at 16:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.