2017-02-04 2 views
4

У меня есть класс, который имеет unordered_set<int> элемент следующим образом:Weird поведения итератора + выдадут ошибку сегментации с unordered_set

У меня есть следующее определение класса с последующим его регулярными & конструкторами копирования, а также некоторые другие функции, изменяющий набор (удалены ненужные сегменты кода, так как класс очень долго):

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

у меня есть другой класс, который содержит экземпляр этого 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, выходной стандартный вывод этой функции, как правило:

Empty positons: 
8 7 6 5 4 3 2 1 
Picked random n: 4 
it begin: 2 
Segmentation fault: 11 

Почему этот segfault? Я понимаю, что есть немного итератора тонко относительно линии empty_positions.erase(i);, но даже когда я прокомментирую это, я получаю такое же поведение.

Я также добавил это право после Picked random n стандартного вывода, и это, а также ошибок сегментации (выход ниже нее):

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); и при этом последняя ручная итерация.

Буду признателен за любую помощь.

Благодаря

+0

ли вы расположены аварии с отладчиком? Где это происходит? У вас есть нулевой указатель или другой указатель, выглядящий неинициализированным или, возможно, указателем на объект вне области видимости? –

+0

Это происходит на 'std :: advance (it, n);' и на последнем сегменте, который у меня есть, где я пытался выполнить ручную итерацию. Ничего не выходит из сферы, нет указателей, если вы перечитаете это. Просто итераторы, контролируемые итераторами C++. – darksky

ответ

2

В HexBoard классе у вас есть:

std::unordered_set<int> emptyPositions(); 

То есть, функция возвращает набор по значению.

Тогда вы позже сделать

auto it = another_board->emptyPositions().begin(); 

Это заставит emptyPositions вернуть временного объекта, тот, который будет разрушенным производственным когда выражение закончено. Это оставит вас с итератором на ключ в уже разрушенном наборе. Разъявление этого итератора приведет к неопределенным поведением.

Решение сделать emptyPositions возвращения постоянной ссылки вместо:

std::unordered_set<int> const& emptyPositions() const; 
3

Я подозреваю, что проблема в том, что emptyPositions() возвращает копию unordered_set. В результате another_board->emptyPositions().begin() возвращает итератор из временного ресурса, срок службы которого не гарантируется. Это, вероятно, очищается, прежде чем вы перебираете его.

Возможно, вы имели в виду, что emptyPositions() вернули ссылку на переменную состояния empty_positions.