简体   繁体   中英

Delete a key/value pair from an unordered_map C++

I have two questions. First I have an std::unordered_map<int,Object*> where Object looks like this:

// Object.hpp
class Object {
    public:
        Object();
        ~Object(){};
        int   Id();
        void setId(int i);
    private:
        int id;
};

This unordered_map is put in a class called DataSet which is used as a container to hold all of these objects. In DataSet I want to be able to properly erase a key/value pair given an integer id from Object.

To do this I tried to create an iterator that finds the key by id, and then deletes the pointer to the iterator, and finally erases the key/value pair. It looks like this:

int DataSet::deleteObject(int id)
{
    std::unordered_map<int,Object*>::iterator found_it = objects.find(id);
    if(found_it != objects.end()) {
        delete *found_it;
        objects.erase(found_it);
    }
    return 1;
}

However, when I compile it I get this error:

dataset.cpp:44:9: error: cannot delete expression of type 'value_type'
      (aka 'pair<key_type, mapped_type>')
        delete *found_it;

Is there a correct way to erase the Object at that location?

Second, when writing a clear() method, I realize I can't just do objects.clear() , so I was wondering if there was a way to actually clear the unordered_map .

For example when I used a std::vector I was able to do this:

std::vector<Object *>::reverse_iterator object;
for(object = objects.rbegin(); object < objects.rend(); object++)
    delete(*object);
objects.clear();

But that won't work now for an unordered_map , so what is the right way to do this? The requirements that I am under make it so I can't change Object , or how the unordered_map is set up.

The first part of the question is quite simple: the iterator reference the value and a given location which is of type std::pair<int const, Object*> . You can't delete such an object. You'll get the pointer using the second part of the object, eg:

delete found_it->second;

However, it is actually quite uncommon and error-prone to take care of this kind of maintenance. You are much better off not storing an Object* but rather a std::unique_ptr<Object> : the std::unique_ptr<Object> will automatically release the object upon destruction. That would also simplify removing an element:

int DataSet::deletObject(int id) {
    return objects.erase(id);
}

When storing std::unique_ptr<Object> you'll probably need to be a bit more verbose when inserting elements into the container, though, eg:

objects.insert(std::make_pair(key, std::unique_ptr<Object>(ptr)));

Given that your Object class doesn't have any virtual functions you could probably just store an Object directly, though:

std::unordered_map<int, Object> objects;

Dereferencing an unordered_map::iterator produces a unordered_map::value_type& , ie pair<const Key, Value>& .

You want

delete found_it->second;
objects.erase(found_it);

Stepping back a bit, are you sure you even need std::unordered_map<int,Object*> instead of std::unordered_map<int,Object> ? If you use the latter you wouldn't have to worry about delete ing anything.

If you do need the mapped_type be an Object* you should use std::unordered_map<int,std::unique_ptr<Object>> . Again, this makes it unnecessary to manually delete the values before erasing an entry from the map.

Also, if Object is the base class for whatever types you're going to be adding to the map, don't forget that it needs a virtual destructor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM