C++ exc_bad_access получает доступ к строковому полю из объекта, на который указывает, в unordered_map

Я пытаюсь поддерживать вектор быстрого доступа следующим образом:

МойКласс.h:

class MyClass{
private:
std::vector<Stuff> myStuffList;
std::tr1::unordered_map<std::string,Stuff*> myStuffListIndex;
...
public:
void addToStuffList(std::string key,Stuff stuff);

};

MyClass.cpp:

...
void MyClass::addToStuffList(std::string name, Stuff stuff){
  myStuffList.push_back(stuff);//our man is guaranteed to be at tail 
  myStuffListIndex[name] = &myStuffList[myStuffList.size()-1];//store 
  //pointer to object that we just copy-constructed at tail of list
}

Материал.ч:

class Stuff{
private:
  std::string name;
public:
  Stuff();
  Stuff(const Stuff&);
  Stuff& operator=(const Stuff&);
  ...

};

Stuff.cpp:

Stuff::Stuff() : name(""){}
Stuff::Stuff(const Stuff& other){
  if(this != &other){
    this->name = other.name;
  }
}
Stuff& Stuff::operator=(const Stuff& other){
  if(this != &other){
    this->name = other.name;
  }
}
std::string Stuff::getName(){
  return name;//exc_bad_access triggered here
}

Позже, когда я пытаюсь получить доступ к элементам из вектора через карту, я получаю явно прерывистую ошибку exc_bad_access следующим образом:

void methodA(){
  Stuff localStuff;
  myClassInstance.addToStuffList("mostrecentstuff",localStuff);
}
...
void methodB(){
  //different method now, localStuff would be out of scope but 
  //shouldn't matter since we passed by value in addToStuffList, right?
  Stuff* pStuff = myStuffListIndex["mostrecentstuff"];
  std::cout << "Hello, my name is " << pStuff->getName() << std::endl;
}

int main(int argc, const char* argv[]){
  methodA();
  methodB();
}

Почему доступ к pStuff->getName() вызывает ex_bad_access?


person CCJ    schedule 27.01.2015    source источник
comment
Не храните указатели на векторные данные и сами изменяйте размер вектора. stackoverflow.com/questions/8261037/.   -  person PaulMcKenzie    schedule 27.01.2015
comment
Побочный вопрос — зачем вам нужно писать определяемый пользователем конструктор копирования и операцию присваивания для Stuff?   -  person PaulMcKenzie    schedule 27.01.2015
comment
@PaulMcKenzie, например, в Stuff нет, но в моем реальном проекте есть; Я подумал, что проблема могла возникнуть из-за ошибки в cc или operator=   -  person CCJ    schedule 28.01.2015
comment
@PaulMcKenzie, что означает «изменить размер вектора»? Вам нужно вызвать изменение размера вручную после push_back?   -  person CCJ    schedule 28.01.2015
comment
Я говорю о том, что вы держитесь за указатели внутри данных вектора. Когда размер вектора изменяется с помощью push_back, эти указатели могут стать недействительными.   -  person PaulMcKenzie    schedule 28.01.2015


Ответы (2)


Как сказал Пол Маккензи, размер вектора можно изменить, а если это так, то его можно переместить по другому адресу. Затем все указатели на предыдущие элементы вектора становятся сломанными.

Вы никогда не должны хранить указатели на элементы в стандартных контейнерах, но для векторов вы можете сохранить их индексы.

Вам придется :

std::vector<Stuff> myStuffList;
std::tr1::unordered_map<std::string,int> myStuffListIndex;

и

myStuffList.push_back(ability);//our man is guaranteed to be at tail 
myStuffListIndex[name] = myStuffList.size() - 1;//store 

Если ваше приложение многопоточное, вам придется защитить приведенный выше код с помощью мьютекса.

person Serge Ballesta    schedule 27.01.2015
comment
ах, это объяснило бы это. Спасибо! - person CCJ; 28.01.2015

Как работает std::vector?
У него есть емкость. Когда эта емкость достигнута, и вы запрашиваете вставку нового элемента, память перераспределяется с дополнительным хранилищем для хранения этого элемента. std::vector затем перемещает свое содержимое из первой ячейки памяти во вновь выделенную.

Таким образом, ваш дизайн сломан (вам нужно будет обновлять карту при каждом изменении размера вектора).

Затем, что касается вашего тестового примера, поскольку вы выполняете только одну вставку, указатель на элемент вектора все еще действителен. Но глядя на ваш метод addToStuffList(), я вижу:

void MyClass::addToStuffList(std::string name, Stuff stuff){
    myStuffList.push_back(ability);//our man is guaranteed to be at tail 
    myStuffListIndex[name] = &myStuffList[myStuffList.size()-1];//store 
    //pointer to object that we just copy-constructed at tail of list
}

Разве не должно быть:

myStuffList.push_back(stuff);

Что такое ability?

person Rerito    schedule 27.01.2015
comment
способность была ошибкой копирования-вставки, извините. Да, это должно было быть вещами - person CCJ; 28.01.2015