简体   繁体   中英

Dynamically allocating into a 2 dimensional std::unordered_map

I have a 2d map declared as

    unordered_map< string, unordered_map<string, Road*>* > matrix;

That takes an instance of Road

class Road {
public:
    Road() : connected(0), weight(0) {}

    bool connected;
    int weight;
};

And I allocate as follows

void addPlace(string place) {
    // error checking
    if (placeExists(place)) {
        cout << "Place already exists" << endl;
        return;
    }

    Road *road = new Road();
    unordered_map<string, Road*> *newRelationship = new unordered_map<string, Road*>;
    newRelationship->insert({ {place},{road} });

    // add to network
    matrix.insert({ { place },{ newRelationship } });
    ++n_verticies;
}

However, when I call

void connectPlace(string source, string dest, int w)
    if (!placeExists(dest) || !placeExists(source)) {
        cout << "Place(s) does not exists" << endl;
        return;
    }
     ...
     if (matrix.find(source)->second->find(dest)->second->connected)

I get an error: "List iterator is not dereferenceable", which suggests to me that I've gone somewhere wrong with my allocation? Thank you in advance.

Here is my call to placeExists, which returns true both calls from connectPlace:

bool placeExists(string place) {
    if (matrix.find(place) == matrix.end()) {
        return false;
    }
    return true;
}

I've broken it down to

    auto a = matrix.find(source);
    auto b = matrix.find(source)->second;
    auto c = matrix.find(source)->second->find(dest); // (<Error reading characters of string.>, {connected=??? weight=??? })
    auto d = matrix.find(source)->second->find(dest)->second; // stops here
    auto e = matrix.find(source)->second->find(dest)->second->connected;

My function calls are as follows

Graph *road_network = new Graph(false);

road_network->addPlace("Sacremento");
road_network->addPlace("Antelope");
road_network->addPlace("Roseville");
road_network->addPlace("San Francisco");
road_network->addPlace("San Jose");
road_network->addPlace("Davis");
road_network->addPlace("Los Angelous");

road_network->connectPlace("Sacremento", "Antelope", 5); //<-- break
road_network->connectPlace("San Francisco", "San Jose", 2);
road_network->connectPlace("Los Angelous", "Davis", 10);
road_network->connectPlace("Davis", "Antelope", 4);
road_network->connectPlace("Roseville", "Davis", 5);
road_network->connectPlace("San Jose", "Antelope", 6);
road_network->connectPlace("Davis", "Los Angelous", 5);

A big part of the problem is that you are trying to do too much all in one line of code.

if (matrix.find(source)->second->find(dest)->second->connected)

This should be broken down into several lines. Specifically, you need to make sure any call to find() actually succeeds before continuing:

auto found = matrix.find(source);
if (found != matrix.end()) {
    // keep going
}
else {
    // print error message
}

Personally, I like this solution better than refactoring your placeExists() for two reasons:

  1. This avoids multiple calls to find() .
  2. This explicitly shows each access of each data structure.
  3. It avoids the assumption that when a key exists in matrix that it also exists in a nested unordered_map .

Where things are going wrong is inside your addPlace() function. Your code:

Road *road = new Road();
unordered_map<string, Road*> *newRelationship = new unordered_map<string, Road*>;
newRelationship->insert({ {place},{road} });

// add to network
matrix.insert({ { place },{ newRelationship } });

creates a new road that could represent a link from the city to itself, but not to any of the others. For instance, after your call to

road_network->addPlace("Sacremento");

your matrix looks like:

  • "Sacremento"
    • "Sacremento": <Road>

And after your call to

road_network->addPlace("Antelope");

it looks like:

  • "Sacremento"
    • "Sacremento": <Road>
  • "Antelope"
    • "Antelope": <Road>

So, later when you try to do road_network->connectPlace("Sacremento", "Antelope", 5); it is looking for an entry with the key "Antelope" in the unordered_map under the key "Sacremento" in the matrix map, which does not exist. So, when you try to dereference the iterator created by matrix.find(source)->second->find(dest) and access its second member, it throws an error because the iterator is not valid.

There are two ways you can fix this:

  1. When addPlace is called, add an entry to the newRelationship for every existing place in the matrix , and add an entry to the unordered_map of every existing place in the matrix for the new place. (This would be pretty inefficient with large datasets, for both storage and processing. Inefficient for storage because it would have to store (number of places)^2 entries, with many of them potentially being unused. Inefficient for processing because every time a new place is added, every single existing place has to be looped through)
  2. In addPlace simply add an empty unordered_map to the matrix under the key place . In connectPlace , if matrix.find(source)->second->find(dest) returns an invalid iterator (determined by if the value returned is equal to matrix.end() ), then add a new entry to the unordered_map under the key source in matrix with key dest and the value being a new Road object with the given weight.

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