lorniper
lorniper

Reputation: 638

Use unique_ptr and appropriate container to do memory management

First of all, my motivation is to do efficient memory management on top of a C like computational kernel. And I tried to use the std::unique_ptr and std::vector, my code looks like below

// my data container
typedef std::unique_ptr<double> my_type;
std::vector<my_type> my_storage;

// when I need some memory for computation kernel
my_storage.push_back(my_type());
my_storage.back.reset(new double[some_length]);

// get pointer to do computational stuff
double *p_data=my_storage.back.get();

Notice here in practice p_data may be stored in some other container(e.g. map) to indexing each allocated array according to the domain problem, nevertheless, my main questions are

  1. Here is std::vector a good choice? what about other container like std::list/set?

  2. Is there fundamental problem with my allocation method?

  3. Suppose after I use p_data for some operations, now I want to release the memory chunk pointed by the raw pointer p_data, what is the best practice here?

Upvotes: 2

Views: 892

Answers (2)

Jack
Jack

Reputation: 133577

First of all, if you are allocating an array you need to use the specialization std::unique_ptr<T[]> or you won't get a delete [] on memory release but a simple delete.

std::vector is a good choice unless you have any explicit reason to use something different. For example, if you are going to move many elements inside the container then a std::list could perform better (less memmove operations to shift things around).

Regarding how to manage memory, it depends mainly on the pattern of utilization. If my_storage is mainly responsible for everything (which in your specification it is, since unique_ptr expresses ownership), it means that it will be the only one who can release memory. Which could be done simply by calling my_storage[i].reset().

Mind that storing raw pointers of managed objects inside other collections leads to dangling pointers if memory is released, for example:

using my_type = std::unique_ptr<double[]>;
using my_storage = std::vector<my_type>;

my_storage data;
data.push_back(my_type(new double[100]));

std::vector<double*> rawData;
rawData.push_back(data[0].get());

data.clear(); // delete [] is called on array and memory is released
*rawData[0] = 1.2; // accessing a dangling pointer -> bad

This could be a problem or not, if data is released by last then there are no problems, otherwise you could store const references to std::unique_ptr so that at least you'd be able to check if memory is still valid, e.g.:

using my_type = std::unique_ptr<double[]>;
using my_managed_type = std::reference_wrapper<const my_type>;
std::vector<my_managed_type> rawData;

Upvotes: 2

Remy Lebeau
Remy Lebeau

Reputation: 596352

Using std::unique_ptr with any STL container , including std::vector, is fine in general. But you are not using std::unique_ptr the correct way (you are not using the array specialized version of it), and you don't need to resort to using back.reset() at all. Try this instead:

// my data container
typedef std::unique_ptr<double[]> my_type;
// or: using my_type = std::unique_ptr<double[]>;

std::vector<my_type> my_storage;

my_type ptr(new double[some_length]);
my_storage.push_back(std::move(ptr));
// or: my_storage.push_back(my_type(new double[some_length]));
// or: my_storage.emplace_back(new double[some_length]);

Upvotes: 1

Related Questions