I am trying to implement a Bayesian estimator for the card game Avalon. The game has five rounds and each round contains at most five proposals made by five different players. If a proposal is accepted, players go on a quest and the game proceeds to the next round. Before the previous round is completed, it is not known which 5 players will get to propose teams in the next round. I wanted to keep track of the current player that gets to propose a team using an iterator, but somehow it ends up pointing to nowhere. Specifically, in the constructor called for round1, the iterator Round::proposer points correctly to &PlayerA, the beginning of Round::proposers. However, when I add this instance (or a copy of it?) to Game::rounds, then the Round member proposer of Game::rounds.back() points nowhere, even though the Round member proposers is still correct. Why does that happen? During execution, of course a read access violation exception is thrown during the call (*Round::proposer)->make_proposal();. I apologize for the lengthy question, but two levels of indirection seemed necessary to produce the error.
// Player.h
#include <string>
class Player
{
private:
std::string name;
public:
Player(std::string name) : name(name) {};
void make_proposal() const {};
};
// Round.h
#include "Player.h"
#include <vector>
class Round
{
private:
std::vector<const Player*> proposers;
std::vector<const Player*>::const_iterator proposer;
public:
Round(std::vector<const Player*> proposers) : proposers(proposers), proposer(Round::proposers.begin()) {};
void next_proposal() { (*Round::proposer)->make_proposal(); };
};
// Game.h
#include "Round.h"
#include <vector>
class Game
{
private:
std::vector<Player*> players;
std::vector<Player*>::iterator active_player;
std::vector<Round> rounds;
public:
Game(std::vector<Player*> players);
void advance_player();
void next_round();
};
// Game.cpp
#include "Game.h"
Game::Game(std::vector<Player*> players)
: players(players), active_player(Game::players.begin())
{
std::vector<Player*>::const_iterator player = Game::players.begin();
std::vector<const Player*> proposers = { *player };
for (unsigned int i = 0; i < 4; ++i) {
++player;
if (player == Game::players.end()) player = Game::players.begin();
proposers.push_back(*player);
}
Round round1(proposers);
Game::rounds = { round1 };
}
void Game::next_round()
{
Game::rounds.back().next_proposal();
}
#include <iostream>
#include "Game.h"
int main()
{
Player playerA("A");
Player playerB("B");
Player playerC("C");
Player playerD("D");
Player playerE("E");
Player playerF("F");
std::vector<Player*> players = { &playerA, &playerB, &playerC, &playerD, &playerE, &playerF };
Game game(players);
for(unsigned int i = 0; i < 5; ++i) {
game.next_round();
}
}
Surprisingly, replacing the two lines of code
Round round1(proposers);
Game::rounds = { round1 };
in Game.cpp with
Round* round1 = new Round(proposers);
Game::rounds = { *round1 };
fixes the issue, although I really don't understand why. After all, rounds is a member variable of Game and exists until instance game is destroyed. Follow-up question to this hack: is the instance to which round1 points in the last code snippet destroyed by the default constructor of class Game since it is dereferenced before adding to the member variable?
Your
Roundcannot be copied without problems:If you do copy it, the
proposerwill still be an iterator to an element in the originalRoundnot to the vector in the copy. When you do this:Then the local object
round1is not destroyed at the end of the scope, hence the iterator that is now insiderounds, after making a copy ofround1, refers to an element that is still alive. Though it refers to the element insideround1, not to theRoundthat you placed inrounds.Either take care of the rule of
3/5forRound, or use indices instead of iterators. Indices get not invalidated when the whole vector is copied. (They also get not invalidated when you push back more elements to the vector, but iterators do)A simpler example for similar problem:
After copying
btoathe pointer inawill still point tob.x, hence output is123(not0as one might expect).