0

I have n points in x axis. At the start of the program I am allocating x with npoints. e.g. x = new double[npoints];

During the simulation npoints may vary. If npoints increases I want to increase the allocated memory. Also, if npoints decreases I want to delete that reduced memory.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162

1 Answers1

10

Use a ::std::vector.

#include <vector>

void foo()
{
    ::std::vector<double> x;
    x.resize(5);
    x[4] = 2.0;
    x.resize(2);
    // etc...
}

The use cases you mention are exactly why ::std::vector was made.

Now, if you resize a vector smaller, it does not normally deallocate memory. This is for a number of reasons, and this StackOverflow question on shrink_to_fit describes why: Is shrink_to_fit the proper way of reducing the capacity a `std::vector` to its size?

But, if you really want to hint to the implementation that the extra points should be deallocated, do this:

#include <vector>

void foo()
{
    ::std::vector<double> x;
    x.resize(5);
    x.shrink_to_fit(); // It didn't get smaller here, but I'm guessing you may
    x[4] = 2.0;        // not know that when you resize the vector in your own code.
    x.resize(2);
    x.shrink_to_fit();
    // etc...
}

The vector still may not actually shrink the allocation. That's an issue to take up with your implementation if it's really a problem.

If it is a problem, and you absolutely have to shrink the allocation and can't get the implementation to be fixed, then you could do this:

#include <iterator>
#include <algorithm>
#include <utility>
#include <vector>

template <class T>
void shrinkwrap_vector(::std::vector<T> &x)
{
   using namespace ::std;
   typedef vector<T> vec_t;

   const auto old_cap = x.capacity();
   x.shrink_to_fit(); // Try shrink_to_fit first to see if it works.
   if ((x.capacity() == old_cap) && (old_cap > x.size())) {
      vec_t t;
      t.reserve(x.size());
      move(x.begin(), x.end(), back_inserter(t));
      swap(x, t);
   }
}

then just call

shrinkwrap_vector(x);

in your code instead of x.shrink_to_fit(). That would just copy your vector into a brand new vector that was as close to the size as your implementation would let you get.

Also note that if you're storing something that has a non-trivial destructor (double has a trivial destructor), that destructor will be called for every dropped element when you do a resize. The whole shrink_to_fit thing is solely about memory allocation, not about construction or destruction.

Lastly, if you really, really want to use the C malloc and realloc calls, you might be able to create a custom vector class that uses those. You have to be extra careful though unless you make your custom class specific to double. You have to call the constructor on any elements that are added after the memory is allocated and the destructor on any dropped elements before the memory is deallocated.

Writing this sort of class is complex. You need to conform to the expectation for container classes in C++ to make it work smoothly with everything else. This includes making iterator classes and things of that nature.

Community
  • 1
  • 1
Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • 3
    Resizing to a lower value will not free up the memory. In case it is critical to free up the memory, you may have to use vector::shrink_to_fit() function. – Jadh4v Jan 02 '17 at 06:42
  • @Jadh4v - That's an interesting design choice they appear to have made. I can't say as I agree with the design choice, but it appears that you are correct. – Omnifarious Jan 02 '17 at 06:53
  • Thanks people. This is really helpful. – Prosun Halder Jan 02 '17 at 07:12
  • 3
    @Omnifarious Otherwise, reducing the size of the vector would invalidate all iterators and all pointers and references to members of the vector. – David Schwartz Jan 02 '17 at 07:13
  • @DavidSchwartz - Well, calling `resize` runs that risk if you don't know if the vector is growing or shrinking. I don't really like depending on a call like that to work in a particular way if the argument is related to a property in one way and work in a different way if the argument is related to a property in a different way. It seems quite fragile and a source of error. – Omnifarious Jan 02 '17 at 07:18
  • 1
    If `shrink_to_fit` doesn't work, and you absolutely need to release memory, `{std::vector t(x.begin(), x.end()); std::swap(t,x);}` has a reasonably high chance of doing so. – Martin Bonner supports Monica Jan 02 '17 at 07:44
  • @MartinBonner - I created a function that did that. – Omnifarious Jan 02 '17 at 07:59
  • @MartinBonner - Now I'm a bit worried about exception safety. But, of course, for doubles that's not an issue. – Omnifarious Jan 02 '17 at 08:11
  • The copy can throw (but that leaves the original unaltered). The swap can't throw (a guarantee of vector). – Martin Bonner supports Monica Jan 02 '17 at 08:22
  • @MartinBonner - If a move in the middle throws, then things are left hanging in an indeterminate (but not broken) state. And that `back_inserter` makes that a distinct possibility. One I try to head off with `reserve` but it's still possible AFAIK. – Omnifarious Jan 02 '17 at 08:23
  • 1
    What move in the middle? My comment only invoked copies. Yes, if you move elements, then you have to be much more careful. (A move operator which throws seems like a bad idea. Vector won't use it for example. ) – Martin Bonner supports Monica Jan 02 '17 at 08:26
  • @MartinBonner - :-) I'm talking about the code I wrote. – Omnifarious Jan 02 '17 at 08:28
  • @MikeMB - I checked. `reserve` *must* actually set capacity to be >= the passed in size. And `push_back` is guaranteed not to allocate unless size == capacity. So as long as `T`'s `move` operation doesn't throw, it should work. – Omnifarious Jan 02 '17 at 08:38
  • @Omnifarious: Sorry, was on my cellphone and overlooked the check (screen cut your code right before the &&) – MikeMB Jan 02 '17 at 08:42