简体   繁体   English

Const_iterator 成员变量在初始化后未指向向量成员变量的开头

[英]Const_iterator member variable not pointing to the begin of a vector member variable after initialization

I am trying to implement a Bayesian estimator for the card game Avalon.我正在尝试为纸牌游戏 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.如果提议被接受,玩家 go 将进行任务,游戏进入下一轮。 Before the previous round is completed, it is not known which 5 players will get to propose teams in the next round.在上一轮完成之前,尚不清楚下一轮将有哪5名球员提出球队。 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 .具体来说,在调用round1的构造函数中,迭代器Round::proposer proposer 正确指向&PlayerA ,即Round::proposers 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.但是,当我将此实例(或它的副本?)添加到Game::rounds时, Game::rounds.back()的 Round 成员proposer无处可去,即使 Round 成员proposers仍然正确。 Why does that happen?为什么会这样? During execution, of course a read access violation exception is thrown during the call (*Round::proposer)->make_proposal();在执行过程中,当然会在调用过程中抛出读访问冲突异常(*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 withGame.cpp

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.毕竟, roundsGame的成员变量,并且存在直到实例game被销毁。 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?这个hack的后续问题:最后一个代码片段中round1指向的实例是否被class Game的默认构造函数破坏,因为它在添加到成员变量之前被取消引用?

Your Round cannot be copied without problems:您的Round无法毫无问题地复制:

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(); };
};

If you do copy it, the proposer will still be an iterator to an element in the original Round not to the vector in the copy.如果你复制它, proposer仍然是原始Round中元素的迭代器,而不是副本中的向量。 When you do this:当你这样做时:

Round* round1 = new Round(proposers);
Game::rounds = { *round1 };

Then the local object round1 is not destroyed at the end of the scope, hence the iterator that is now inside rounds , after making a copy of round1 , refers to an element that is still alive.然后本地 object round1在 scope 结束时不会被破坏,因此现在在rounds内部的迭代器,在制作了round1的副本之后,指的是仍然活着的元素。 Though it refers to the element inside round1 , not to the Round that you placed in rounds .尽管它指的是round1内的元素,而不是您放置在rounds中的Round

Either take care of the rule of 3/5 for Round , or use indices instead of iterators.要么注意Round3/5规则,要么使用索引而不是迭代器。 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:类似问题的更简单示例:

#include <iostream>
struct broken {
    int x;
    int* ptr;
    broken(int a = 0) : x(a),ptr(&x) {}
};

int main() {
    broken a{42};
    broken b{123};
    a = b;
    a.x = 0;
    std::cout << *(a.ptr);
}

After copying b to a the pointer in a will still point to bx , hence output is 123 (not 0 as one might expect).b复制到a后, a 中a指针仍将指向bx ,因此 output 为123 (而不是预期的0 )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM