奇怪的迭代器行为 + unordered_set 的段错误
Weird iterator behaviour + segfault with unordered_set
我有一个 class 有一个 unordered_set<int>
成员如下:
我有以下 class 定义,后面是它的常规和复制构造函数,以及一些其他修改集合的函数(删除不相关的代码段,因为 class 很长):
#include <iostream>
#include <unordered_set>
#include <random>
class HexBoard {
public:
HexBoard(int n);
HexBoard(const HexBoard &obj);
std::unordered_set<int> emptyPositions();
private:
std::unordered_set<int> empty_positions;
};
HexBoard::HexBoard(int n) {
for (int i = 0; i < n * n; i++) {
empty_positions.insert(i);
}
}
HexBoard::HexBoard(const HexBoard &obj) : empty_positions(obj.empty_positions) {};
void HexBoard::placeStone(int i) {
checkBounds(i); // raises an error if i >= n
empty_positions.erase(i);
}
std::unordered_set<int> HexBoard::emptyPositions() {
return empty_positions;
}
我有一个不同的 class,其中包含此 HexBoard 的一个实例。它有一个函数,可以使用复制构造函数将该板复制到不同的变量中:
class Game {
public:
Game(HexBoard::HexBoard *board) : board(board) {};
private:
HexBoard *board;
void monteCarlo(int position);
};
void Game::monteCarlo(int position) {
HexBoard *another_board = new HexBoard(*board);
int count = 0;
while (count < 5) {
count++;
std::uniform_int_distribution<unsigned> dis(
0, another_board->emptyPositions().size() - 1
);
std::cout << "Empty positons:\n";
for (const auto& pos : another_board->emptyPositions()) {
std::cout << pos << " ";
}
std::cout << "\n";
int n = dis(gen);
std::cout << "Picked random n: " << n << "\n";
auto it = another_board->emptyPositions().begin();
std::cout << "it begin: " << *it << "\n";
std::advance(it, n);
std::cout << "it advance: " << *it << "\n";
int absolute_position = *it;
std::cout << "picked " << absolute_position << "\n";
}
}
在monteCarlo
函数中,假设emptyPositions
设置的内容最初是8, 7, 6, 5, 4, 3, 2, 1
,这个函数的stdout输出通常是:
Empty positons:
8 7 6 5 4 3 2 1
Picked random n: 4
it begin: 2
Segmentation fault: 11
为什么会出现这个段错误?我知道关于 empty_positions.erase(i);
行有一些巧妙的迭代器,但即使我将其注释掉,我也会得到相同的行为。
我还在 Picked random n
stdout 和这个段错误之后添加了这个(输出在它下面):
std::cout << "set buckets contain:\n";
for ( unsigned i = 0; i < ai_board->emptyPositions().bucket_count(); ++i) {
std::cout << "bucket #" << i << " contains:";
for ( auto j = ai_board->emptyPositions().begin(i);
j != ai_board->emptyPositions().end(i); ++j)
std::cout << " " << *j;
std::cout << std::endl;
}
输出:
set buckets contain:
Segmentation fault: 11
段错误发生在 std::advance(it, n);
和最后一次手动迭代时。
如有任何帮助,我将不胜感激。
谢谢
我怀疑问题是 emptyPositions()
正在 return 复制 unordered_set
。因此,another_board->emptyPositions().begin()
returns 是一个来自临时对象的迭代器,其生命周期无法保证。在您遍历它之前,它可能正在清理。
您可能打算 emptyPositions()
return 引用状态变量 empty_positions
。
在 HexBoard
class 你有:
std::unordered_set<int> emptyPositions();
即函数return是一个值的集合。
那你以后再做
auto it = another_board->emptyPositions().begin();
这将导致emptyPositions
到return一个临时对象,一旦表达完毕。这将为您留下一个迭代器,指向现在已破坏的集合中的一个键。取消引用此迭代器将导致 未定义的行为。
解决方案是使 emptyPositions
return 成为常量引用:
std::unordered_set<int> const& emptyPositions() const;
我有一个 class 有一个 unordered_set<int>
成员如下:
我有以下 class 定义,后面是它的常规和复制构造函数,以及一些其他修改集合的函数(删除不相关的代码段,因为 class 很长):
#include <iostream>
#include <unordered_set>
#include <random>
class HexBoard {
public:
HexBoard(int n);
HexBoard(const HexBoard &obj);
std::unordered_set<int> emptyPositions();
private:
std::unordered_set<int> empty_positions;
};
HexBoard::HexBoard(int n) {
for (int i = 0; i < n * n; i++) {
empty_positions.insert(i);
}
}
HexBoard::HexBoard(const HexBoard &obj) : empty_positions(obj.empty_positions) {};
void HexBoard::placeStone(int i) {
checkBounds(i); // raises an error if i >= n
empty_positions.erase(i);
}
std::unordered_set<int> HexBoard::emptyPositions() {
return empty_positions;
}
我有一个不同的 class,其中包含此 HexBoard 的一个实例。它有一个函数,可以使用复制构造函数将该板复制到不同的变量中:
class Game {
public:
Game(HexBoard::HexBoard *board) : board(board) {};
private:
HexBoard *board;
void monteCarlo(int position);
};
void Game::monteCarlo(int position) {
HexBoard *another_board = new HexBoard(*board);
int count = 0;
while (count < 5) {
count++;
std::uniform_int_distribution<unsigned> dis(
0, another_board->emptyPositions().size() - 1
);
std::cout << "Empty positons:\n";
for (const auto& pos : another_board->emptyPositions()) {
std::cout << pos << " ";
}
std::cout << "\n";
int n = dis(gen);
std::cout << "Picked random n: " << n << "\n";
auto it = another_board->emptyPositions().begin();
std::cout << "it begin: " << *it << "\n";
std::advance(it, n);
std::cout << "it advance: " << *it << "\n";
int absolute_position = *it;
std::cout << "picked " << absolute_position << "\n";
}
}
在monteCarlo
函数中,假设emptyPositions
设置的内容最初是8, 7, 6, 5, 4, 3, 2, 1
,这个函数的stdout输出通常是:
Empty positons:
8 7 6 5 4 3 2 1
Picked random n: 4
it begin: 2
Segmentation fault: 11
为什么会出现这个段错误?我知道关于 empty_positions.erase(i);
行有一些巧妙的迭代器,但即使我将其注释掉,我也会得到相同的行为。
我还在 Picked random n
stdout 和这个段错误之后添加了这个(输出在它下面):
std::cout << "set buckets contain:\n";
for ( unsigned i = 0; i < ai_board->emptyPositions().bucket_count(); ++i) {
std::cout << "bucket #" << i << " contains:";
for ( auto j = ai_board->emptyPositions().begin(i);
j != ai_board->emptyPositions().end(i); ++j)
std::cout << " " << *j;
std::cout << std::endl;
}
输出:
set buckets contain:
Segmentation fault: 11
段错误发生在 std::advance(it, n);
和最后一次手动迭代时。
如有任何帮助,我将不胜感激。
谢谢
我怀疑问题是 emptyPositions()
正在 return 复制 unordered_set
。因此,another_board->emptyPositions().begin()
returns 是一个来自临时对象的迭代器,其生命周期无法保证。在您遍历它之前,它可能正在清理。
您可能打算 emptyPositions()
return 引用状态变量 empty_positions
。
在 HexBoard
class 你有:
std::unordered_set<int> emptyPositions();
即函数return是一个值的集合。
那你以后再做
auto it = another_board->emptyPositions().begin();
这将导致emptyPositions
到return一个临时对象,一旦表达完毕。这将为您留下一个迭代器,指向现在已破坏的集合中的一个键。取消引用此迭代器将导致 未定义的行为。
解决方案是使 emptyPositions
return 成为常量引用:
std::unordered_set<int> const& emptyPositions() const;