【问题标题】:Const_iterator member variable not pointing to the begin of a vector member variable after initializationConst_iterator 成员变量在初始化后未指向向量成员变量的开头
【发布时间】:2021-05-18 04:57:30
【问题描述】:

我正在尝试为纸牌游戏 Avalon 实现贝叶斯估计器。游戏有五轮,每轮最多包含五个不同玩家提出的五个建议。如果提议被接受,玩家将继续进行任务,游戏将进入下一轮。在上一轮完成之前,尚不清楚下一轮将有哪5名球员提出球队。我想跟踪使用迭代器提议团队的当前玩家,但不知何故它最终指向无处。具体来说,在调用round1 的构造函数中,迭代器Round::proposer 正确指向&PlayerA,即Round::proposers 的开头。但是,当我将此实例(或它的副本?)添加到Game::rounds 时,Game::rounds.back() 的 Round 成员 proposer 无处可去,即使 Round 成员 proposers 仍然正确。为什么会这样?在执行过程中,当然会在调用(*Round::proposer)->make_proposal(); 期间抛出读访问冲突异常。对于这个冗长的问题,我深表歉意,但似乎需要两个级别的间接来产生错误。

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

奇怪的是,替换了两行代码

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

Game.cpp

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

解决了这个问题,虽然我真的不明白为什么。毕竟roundsGame 的成员变量,并且一直存在直到实例game 被销毁。此 hack 的后续问题:round1 在最后一个代码 sn-p 中指向的实例是否被 Game 类的默认构造函数破坏,因为它在添加到成员变量之前被取消引用?

【问题讨论】:

  • 迭代器很容易失效 使用索引。
  • 副手,似乎proposer 应该是Player*

标签: c++ iterator member const-iterator


【解决方案1】:

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

如果您复制它,proposer 仍将是原始 Round 中元素的迭代器,而不是副本中的向量。当你这样做时:

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

那么本地对象round1在作用域的末尾没有被销毁,因此现在在rounds内部的迭代器在复制round1之后引用了一个仍然存在的元素。虽然它指的是round1 中的元素,而不是您放置在rounds 中的Round

要么为Round 处理3/5 的规则,要么使用索引而不是迭代器。复制整个向量时,索引不会失效。 (当您将更多元素推回向量时,它们也不会失效,但迭代器会这样做)


类似问题的更简单示例:

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

在将b 复制到a 之后,a 中的指针仍将指向b.x,因此输出为123(不是人们所期望的0)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-22
    • 2012-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多