2015-11-30 8 views
0

Кодекса перечислены ниже вызывает ошибку сегментации в петле на основе итератора:вина Сегментация итератора разыменования

#include <iostream> 
#include <vector> 

class A { 
public: 
    A(unsigned id = 0) {id_ = id;} 
    unsigned get_id() {return id_;} 
private: 
    unsigned id_; 
}; 

class B { 
public: 
    B() {} 
    B(std::vector<A*> entries) : entries_(entries) {} 
    const std::vector<A*> get_entries() const { 
     return entries_; 
    } 
private: 
    std::vector<A*> entries_; 
}; 

int main() { 
    std::vector<A*> entries; 
    for (unsigned i = 0; i < 5; i++) { 
     entries.push_back(new A(i)); 
    } 
    B b(entries); 

    // index based access (ok) 
    for (unsigned i = 0; i < b.get_entries().size(); i++) { 
     std::cout << b.get_entries()[i]->get_id() << std::endl; 
    } 

    // iterator based access (segmentation fault) 
    for (std::vector<A*>::const_iterator i = b.get_entries().begin(); 
     i != b.get_entries().end(); 
     ++i) { 
     std::cout << (*i)->get_id() << std::endl; 
    } 
} 

С другой стороны, индекс на основе цикла работает нормально.

Это поведение срабатывает, когда возвращается копия std::vector (см.: const std::vector<A*> get_entries() const), а не ссылка const на нее, например. const std::vector<A*>& get_entries() const. Последний случай работает нормально.

Как можно объяснить это поведение?

+1

Объект, возвращающийся из get_entries(), создает итератор и затем уничтожается. Впоследствии вы пытаетесь разыменовать итератор на уничтоженном контейнере и сбой. Это происходит потому, что объекты, которые возвращаются по значению и не хранятся в любом месте, имеют очень короткий срок службы. – Shloim

+1

Тот же вопрос, что и этот: http://stackoverflow.com/questions/30041907/can-i-use-nested-loops-with-vectors-in-cpp – PaulMcKenzie

ответ

2

С get_entries() возвращает вектор по значению, вы используете каждый другой объект std::vector<A*>. Сравнение итераторов от разных векторов - это неопределенное поведение, поэтому даже с get_entries().begin() != get_entries().end() у вас уже проблемы.

1

Проблема заключается в том, что get_entries возвращает временную копию вектора, а не ссылку на оригинал. Поэтому каждый раз, когда вы вызываете его и захватываете итератор во временное возвращаемое значение, этот итератор будет недействителен к моменту его использования. Это приводит к неопределенному поведению, в котором крах довольно распространен.

У вас есть два варианта.

Вариант 1: Храните возвращенный вектор в локальной переменной и перебирайте его.

Вариант 2: Измените функцию, чтобы вернуть ссылку const std::vector<A*> & get_entries() const.