Reputation: 105
I have some dynamic int * arrays that I would like to use as keys for an unordered_map. I'm a bit unclear on how I should declare the key type so it would actually be the value of the entire array.
Also, to deallocate the memory for the arrays, do I use map.clear()
?
Example:
unordered_map<??, int> frequency;
while (some_condition) {
int *my_array = new int[size];
putSomeValuesToArray(my_array);
frequency[my_array]++;
}
// to deallocate memory for the arrays in frequency?
Upvotes: 2
Views: 3196
Reputation: 35449
You won't be able to stick int*
as key inside a std::map
and still be able to retrieve elements 'by value'. The reason for that is a single int*
only provides the start of an array, you need the end too to compute anything (equivalently and more C-like, the length). In other words, int*
does not an array (dynamic or otherwise) make; in this context it's an iterator to the start of a sequence.
You can use std::pair<int*, int*>
but it should only be used for non-owning 'views' of data. That is, if you manually manage memory with a std::map<std::pair<int*, int*>, int>
you'll end up with a headache. One possibility is using a smart-pointer: std::pair<std::unique_ptr<int[]>, int*>
. But as others have recommended, just use std::vector
as it is still compatible with C-like interfaces that deal with int*
. Plus a const std::pair<std::unique_ptr<int>, int*>
still allows you to scribble the memory, which can mess up the ordering of the map and ends you up in trouble.
A final blow to using int*
or std::unique_ptr<int[]>
is that you would need to provide the strict weak ordering that std::map
requires, whereas std::vector<int>
comes with an appropriate operator<
. On the other hand, you need to provide a hash for both if you settle instead on std::unordered_map
. For what it's worth, a simple functor that uses std::lexicographical_compare
(same semantics as std::vector
comparison):
struct compare {
typedef std::pair<std::unique_ptr<int[]>, int*> value_type;
bool
operator()(value_type const& lhs, value_type const& rhs) const
{
return std::lexicographical_compare(lhs.first.get(), lhs.second
, rhs.first.get(), rhs.second);
}
};
Then you can use std::map<std::pair<std::unique_ptr<int[]>, int*>, int, compare>
.
Upvotes: 0
Reputation: 69988
If you meant,
int *p = new int[size];
and you want to make p
as a key, then for you better choice will be to use std::set()
.
Also, you cannot simply clear()
it; you may want to delete[]
each element before, to avoid memory leak. If you don't want to individually delete[]
it then, you can use shared_ptr
(or other smart pointers) which will do the job for you.
Upvotes: 1
Reputation: 299780
Important: If you use a dynamically allocated object in a STL Container, then to deallocate the memory you need to walk the container and call delete
(or delete[]
) explicitly.
I would strongly suggest moving from int*
to std::vector<int>
, you would not have the issue of memory ownership any longer then.
In order to declare a key, pass the type as the template parameter:
std::unordered_map<int*, Foo>
std::unordered_map<std::vector<int>, Foo>
Of course, for unordered_map
you are likely going to need a specific Hash
parameter, which derives a hash value from the Key
you pass.
Upvotes: 4