2013-06-09 4 views
3

У меня есть вектор указателей объектовSegfault когда разыменования итератора для вектора указателей

std::vector<Element*> elements; 

При переборе вектора, я хотел бы удвоить разыменования итератора для вызова методов объекта.

std::cout << (*it)->getName() << std::endl; 

Это приводит к segfault. Соответствующий код приведен ниже.

Я думаю, что проблема в том, как я инициализирую вектор, потому что я мог бы перемещать for-loop в метод initialize(), и он отлично работает. В takeTurn() вектор имеет соответствующий размер, а указатели содержат правильные адреса. Означает ли это, что объекты, на которые указывают, подвергаются преждевременному уничтожению?

main.cpp:

#include <vector> 
#include <iostream> 
#include "Element.h" 

    std::vector<Element*> elements; 

void initialize() { 
    Element ice = Element("ice",1); 
    Element fire = Element("fire",2); 
    elements.push_back(&ice); 
    elements.push_back(&fire); 
} 

void takeTurn() { 
    std::vector<Element*>::iterator it; 
    for(it = elements.begin(); it != elements.end(); ++it) { 
     std::cout << (*it)->getName() << std::endl; 
    } 
} 

int main() { 
    initialize(); 
    takeTurn(); 
    return 0; 
} 

Element.h:

#include <string> 

class Element { 
    public: 
     Element(std::string name, int id); 
     int getID() { return id_; } 
     std::string getName() { return name_; } 

    private: 
     int id_; 
     std::string name_; 
}; 

Element.cpp:

#include "Element.h" 

Element::Element(std::string name, int id) { 
    name_ = name; 
    id_ = id; 
} 

ответ

5

Ваша функция инициализации сломана. Вы создаете локальные объекты, а затем нажимаете их адреса на вектор. Но когда функция возвращается, эти объекты уничтожаются, а указатели более недействительны. Самое простое исправление, если вам не нужен полиморфизм, - это просто создать вектор объектов Element вместо указателей.

std::vector<Element> elements; 
... 
elements.push_back(Element("ice",1)); 
elements.push_back(Element("fire",2)); 

Если вам нужен полиморфизм, используйте интеллектуальные указатели.

std::vector<std::unique_ptr<Element>> elements; 
... 
elements.push_back(std::unique_ptr<Element>(new Element("ice",1))); 
elements.push_back(std::unique_ptr<Element>(new Element("fire",2))); 

Если вы должны были продолжать использовать сырые указатели, то вам нужно будет каким-то образом, чтобы обеспечить сохранение объектов, возможно, выделяя их с new. Затем вам нужно будет убедиться, что вы вызываете delete по каждому из этих указателей, с которыми вы закончили. Я не рекомендую этот маршрут.

+1

Все ответы были замечательными, но мне понадобится вектор указателей, поэтому я собираюсь принять этот ответ. Спасибо за дополнительную информацию. –

1

Нажимаешь оборванных указатели в ваш вектор:

void initialize() { 
    Element ice = Element("ice",1); 
    Element fire = Element("fire",2); 
    elements.push_back(&ice); 
    elements.push_back(&fire); 
} 

здесь лед и огонь - местная переменная. Вы нажимаете адрес на вектор, а затем, когда окончательный} достигнут, оба будут уничтожены. Позже, когда вы переигрываете, это неправильное поведение указателя не определено.

5

Вы передаете указатели на локальные переменные к вектору здесь:

Element ice = Element("ice",1); 
Element fire = Element("fire",2); 
elements.push_back(&ice); 
elements.push_back(&fire); 

При выходе из функции, ice и fire перестает существовать, так что вы остались с оборванными указателями.

Решение этой проблемы зависит от того, нужен ли вам вектор указателей. Это может быть проще иметь std::vector<Element>:

std::vector<Element> elements; 

затем

elements.push_back(Element("ice",1)); 
elements.push_back(Element("fire",2)); 
1

Ваш вектор хранит указатели на локальные переменные, созданные в стеке. Когда функция будет закончена, память, занятая этими переменными, будет восстановлена. Если вы попытаетесь получить доступ к памяти, вы получите segfault.

void initialize() { 
    Element ice = Element("ice",1); // Local variable. 
    Element fire = Element("fire",2); // Local variable. 
    elements.push_back(&ice); 
    elements.push_back(&fire); 
} // Ice and fire disappear. 

Распределить память для элементов в куче:

void initialize() { 
    Element *ice = new Element("ice",1); 
    Element *fire = new Element("fire",2); 
    elements.push_back(ice); 
    elements.push_back(fire); 
} 

Помните, чтобы освободить память, когда вы закончили!

typedef std::vector<Element *>::iterator EIter; 
for (EIter it = elements.begin(); it != elements.end(); ++it) { 
    delete *it; 
} 
Смежные вопросы