46

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.

Community
  • 1
  • 1
Jacob
  • 34,255
  • 14
  • 110
  • 165
  • 12
    Why `assert(vec != NULL)`? `new` will not return `NULL`. – James McNellis Jun 16 '10 at 15:11
  • 6
    Some older implementations (or compilers without exceptions!) do – Martin Beckett Jun 16 '10 at 15:12
  • 1
    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. – Tyler McHenry Jun 16 '10 at 15:14
  • 1
    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. – Paul Rubel Jun 16 '10 at 15:18
  • 5
    The second (and the only right) variant certainly doesn't need the `vec.clear();`. – sbi Jun 16 '10 at 15:26
  • 2
    @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();` will suffice. – Mike Seymour Jun 16 '10 at 16:41
  • 1
    @Jacob: Perhaps it would be good to edit your first solution to use the `nothrow` form of `new`, i.e. `std::vector *vec = new (nothrow) std::vector;`, or simply drop the `assert()`. That would address the comments @James and @Martin made. – Void - Othman Jun 16 '10 at 18:08
  • @Mike: I thought the standard idiom was `vec.swap(std::vector())`? That wouldn't need a `clear()`. – sbi Jun 16 '10 at 19:35
  • 1
    @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. – AnT stands with Russia Jun 17 '10 at 18:21
  • 3
    @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?" – James McNellis Jun 17 '10 at 18:45
  • 1
    @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. – AnT stands with Russia Jun 17 '10 at 19:04

12 Answers12

56

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.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 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. – Billy ONeal Jun 16 '10 at 15:25
  • @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 function – Jacob Jun 16 '10 at 15:30
  • 1
    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. – Martin Beckett Jun 16 '10 at 15:34
  • 13
    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. – Pedro d'Aquino Jun 16 '10 at 17:10
  • 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. – v.oddou Nov 18 '14 at 07:21
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.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • is this any different than `vec.resize(0)`? – pretzlstyle May 01 '19 at 21:10
  • 2
    ... or the clearer `vec.clear()`? (No pun intended) – L. F. May 13 '19 at 11:41
  • 4
    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. – devnullicus Mar 06 '20 at 22:26
  • This style I think has the benefit of readability over the swap method that is shown. JMO – Morc Nov 01 '22 at 13:23
13
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();
deft_code
  • 57,255
  • 29
  • 141
  • 224
  • 6
    `shrink_to_fit` is just a hint, actual shrinking is not guaranteed. – fredoverflow Jun 16 '10 at 17:02
  • 3
    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. – deft_code Jun 16 '10 at 17:51
  • 8
    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. – entheh Feb 18 '14 at 18:45
  • I think it should be mentioned why does this work. It is related to the copy and swap idiom. Basically with `std::vector().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. – plasmacel Jan 18 '17 at 02:59
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();
Sam
  • 31
  • 1
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.

Winston Ewert
  • 44,070
  • 10
  • 68
  • 83
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.

Patrick
  • 23,217
  • 12
  • 67
  • 130
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.

Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
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.

Ben313
  • 1,662
  • 3
  • 20
  • 32
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().

Alan
  • 4,897
  • 2
  • 24
  • 17
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.

Cubbi
  • 46,567
  • 13
  • 103
  • 169
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);
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
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());
Graphics Noob
  • 9,790
  • 11
  • 46
  • 44