C++ Map throws vector subscript out of range

1.2k Views Asked by At

I am trying to create a class which stores its instances in a map like so:

class Apple {
    public: 
        static Apple* getApple(const std::string &name) {
             auto it = allApples.find(name);

             if (it == allApples.end()) // if this apple doesnt exist, make a new one
                 return new Apple(name); 
             else // if the apple exists, return its pointer
                 return it->second;
        }
    private:
        static std::unordered_map<std::string, Apple*> allApples =                
            std::unordered_map<std::string, Apple*>();

        Apple(const std::string &name) {
            // create an apple
            allApples.insert({ name, this });
        }
}

Now, I made classes which store static Apples like so:

class Orange {
     static Apple *myOrangeApple;
}

myOrangeApple = Apple::getApple("orange");

When I run the program, it crashes on the first line of the getApple() method with a vector subscript out of range error. I have tried looking around but I can't really find any solution which deals with maps and this out of range error. My best guess from some of the research I did is that it is something about the static order initialization, but I really am not too sure.

3

There are 3 best solutions below

1
On BEST ANSWER

Use a function-scope static object.

...
private:
   std::unordered_map<std::string, Apple*>& allApples() {
      static std::unordered_map<std::string, Apple*> apples;
      return apples;
   }

This is one of standard ways to fight the static initialization order fiasco. It works because a block-scope static object is guaranteed to be initialised when the block is executed for the first time.

This method will not work if there are mutual dependencies between objects managed this way.

2
On

First thing your getApple method should be static and you should initialize the class member (static member) outside the class. Try this -

class Apple {
    public: 
        static Apple* getApple(const std::string &name) {
             auto it = allApples.find(name);

             if (it == allApples.end()) // if this apple doesnt exist, make a new one
                 return new Apple(name); 
             else // if the apple exists, return its pointer
                 return it->second;
        }
    private:
        static std::unordered_map<std::string, Apple*> allApples; 

        Apple(const std::string &name) {
            // create an apple
            allApples.insert({ name, this });
        }
};

std::unordered_map<std::string, Apple*> Apple::allApples = std::unordered_map<std::string, Apple*>();

class Orange {
     static Apple *myOrangeApple;
};

Apple * Orange::myOrangeApple=Apple::getApple("orange");
5
On

You're accessing allApples before it's been initialized. Until you enter main, there's no guarantee that allApples has been constructed, so accessing it is a definite no-no.

Having classes that add themselves to containers is a bad idea, but doing it in a global object is truly awful. Don't do that.