Search code examples
c++visual-studio-2017sdl-ttf

std::map contains value from last instance before it's told to store said value


So recently I was experimenting with some code and found an incredibly strange error. I've created an std::map for storing fonts for my program. However, through a long session of debugging, I managed to find out that this map was storing a value (NULL) with the key "arial" before I emplaced "arial" within it. So I also found that when I change the program so that it never emplaces "arial" within the map, the map still contains "arial".

Here is a basic recreation of the class hierarchy I have set up:

#include "SDL_ttf.h"
#include <map>
#include <string>
#include <array>

class AssetManager{
public:
    void addFont(std::string ID, std::string path, int fontSize){
        std::cout <<""; //if we put a breakpoint here, the first time it stops, "arial" is already defined within fonts.
        fonts.emplace(ID, TTF_OpenFont(path, fontSize);
    }
private:
std::map<std::string, TTF_Font*> fonts;
};

class Game{
public:

void init(){
    if(TTF_Init() == -1){
    std::cout << "Failed to init SDL_TTF" << std::endl; //this has never gotten called; TTF always succeeds in initializing
    }
    assets->addFont("fontname", "assets/Arial.ttf", 16);
}

static shared_ptr<AssetManager>(new AssetManager) assets;

};

main(int argc, char* argv[]){
    Game *game = nullptr;
    game = new Game;
    game->init();
}

As you can see, in this program, we never add a font called "arial", and yet for some reason (I'd assume it's something along the lines of a cache error), fonts contains data for "arial" before it was ever told, in this instance of the program, what "arial" is. In other words, "arial" was defined in the previous instance, but never in this instance, but the map still has a key for "arial". If anybody knows why or how such a thing can occur I'd love to hear it.


Solution

  • I'm guessing you are using std::maps operator[]. That operator will not just look up an existing entry. It will create the entry if it's not already there, with a default constructed (actually "value initialized") value.

    This is actually a feature in many cases. For example it allows you to do something like the following to count occurances of characters in a string:

    std::map<char, int> result;
    for (const auto& c : some_string) {
        ++result[c];
    }
    

    If you just want to look up a key, without the insertion behaviour, then you have several options:

    std::map::at

    std::map::find

    std::map::contains

    And others.