When I try to insert an object into my map, it throws the following exception:
Unhandled exception at 0x77a015de in Main.exe: 0xC0000005: Access violation reading location 0x00000004.
The "reading location 0x00000004" makes me think it's some sort of null pointer exception. However, the map itself is a static map, initialized in the .cpp file. Why wouldn't it have a location when trying to do the insert?
Here's the class. It's a player class that manages mapping IDs to players:
// Player.hpp
class Player {
public:
Player(){}; // I had to make this public, otherwise it wouldn't compile.
Player(Player &p);
Player & operator=(const Player &p);
bool operator==(const Player &p);
int getID() const;
int getTeam() const;
string getName() const;
Vec3 getColor() const;
static Player newPlayer(int team, string name, Vec3 color);
private:
Player(int id, int team, string name, Vec3 color);
int id,
team;
string name;
Vec3 color;
static std::map<int, Player> players;
};
and the cpp file:
#include "Player.hpp"
std::map<int, Player> Player :: players;
int Player :: currentPlayer=-1;
// Constructor
Player :: Player(Player &p) : id(p.getID()),
team(p.getTeam()),
name(p.getName()),
color(p.getColor()){}
Player & Player :: operator=(const Player &p){
if (this==&p){
return *this;
}
id=p.getID();
team=p.getTeam();
name=p.getName();
color=p.getColor();
return *this;
}
bool Player :: operator==(const Player &p){
return p.getID()==getID();
}
// Factory
Player Player :: newPlayer(int team,
string name,
Vec3 color){
int playerID=0;
if (players.size()>0){
int playerID=(*players.rbegin()).first+1; // get an ID higher than the largest already there.
}
Player p(playerID, team, name, color);
players.insert(std::make_pair(playerID, p)); // EXCEPTION THROWN HERE
return players[playerID];
}
// Internal Constructor
Player :: Player(int id,
int team,
string name,
Vec3 color) : id(id),
team(team),
name(name),
color(color){}
Can anyone help me understand what's going on here?
EDIT: The code that's calling this method is in main.hpp, outside of any methods, in the global scope:
Player player1=Player::newPlayer(1, "p1", Vec3(0.2, 0.2, 0.8)),
player2=Player::newPlayer(2, "p2", Vec3(0.8, 0.2, 0.2));
To address the need for a public default constructor, try changing this:
Player p(playerID, team, name, color);
players.insert(std::make_pair(playerID, p)); // EXCEPTION THROWN HERE
return players[playerID];
into this:
return players.insert(std::make_pair(playerID, Player(playerID, team, name, color))).first->second;
(Explanation: The []-operator requires that your mapped type be default-constructible. Using it means that you're ignoring the knowledge that the key that you're looking up exists in the map. The operator wants to create a new default-constructed element if the key that you give it doesn't exist.)
Update: You also have to address the static initialization problem. Here's one way how to do this:
// header:
class Player {
public:
static std::map<int, Player> & players();
// ...
};
// implementation:
std::map<int, Player> & Player::players()
{
static std::map<int, Player> impl;
return impl;
}
Now just use Player::players() to get a reference to the map.
Is the insertion also happening as part of the static initialisation of some other object?
If so you've hit a classic C++ gotcha: the order of static initialisation between different files is largely undefined, and rarely does what you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With