Лоша практика ли е връщането на препратка към елемент в нарастващ вектор?

Изпитвам странен бъг в паметта (segfault). След известен преглед на кода открих, че предавам указател към себе си (this) към стойност на член вътре в клас, който сам по себе си е във вектор и е на път да промени адреса си. Тогава (евентуално) векторът нараства и указателят, който току-що премина, става невалиден.

Разбирам, че std::vector трябва да премести съдържанието си в паметта, тъй като е гарантирано, че продължава, нали? Мислех, че мога да преодолея това, като предам const препратка вместо указател (предавайки *this). Но аз имам същата грешка. Не виждам защо препратките могат да станат невалидни точно както указателите! Ситуацията може да се обобщи така:

class Foo
{
public:
    Foo(): x(new Bar(*this)){}
    Foo(Foo&&);
    Foo& operator=(Foo&&)
private:
    // some heavy to move data
    Bar* x;
};

class Bar
{
public:
    Bar(const Foo& foo): instance(foo){}
private:
    const Foo& instance;
};

std::vector<Zoo> vec;
// Do some stuff on vec, then x->instance become invalid. Why?

person sorush-r    schedule 31.05.2014    source източник
comment
„Но аз имам същата segfault.“ Това очевидно прави това невалидно като добра практика!   -  person πάντα ῥεῖ    schedule 31.05.2014
comment
Къде се декларира Zoo и какво точно правите с vector<Zoo>? Да, елементът, връщащ кода в нарастващ вектор чрез препратка, може да се взриви при първото преразпределение на вътрешната памет, което се случва във вектора (при размери 0, 1, 2, 4 и т.н. по подразбиране).   -  person bobah    schedule 31.05.2014
comment
Потенциално можете да се справите с това, като запазите достатъчно място във вектора, така че той никога да не расте, но като цяло това, което описахте, е бомба със закъснител, която чака да избухне.   -  person Retired Ninja    schedule 31.05.2014
comment
Връщането на препратка към елемент в нарастващ вектор лоша практика ли е? Това е само ако съхранявате тази препратка за бъдеща употреба. Нищо в публикувания от вас код обаче не казва къде и как правите това.   -  person R Sahu    schedule 31.05.2014
comment
@RetiredNinja Запазването на достатъчно място не е опция за мен. Ще използвам std::list (с линейно време за достъп!)   -  person sorush-r    schedule 31.05.2014
comment
@RSahu Да, съхранявам справката за в бъдеще. Всъщност, след като парсерите са направили структурите от данни, алгоритъмът трябва да работи с тях.   -  person sorush-r    schedule 31.05.2014


Отговори (1)


Когато вектор трябва да преразпредели своето хранилище, обектите, които са живели на „старото“ местоположение, се изтриват, след като са били копирани (или преместени в) новото хранилище. Преместването не „телепортира“ обект на ново място, то все още създава нов обект на новото място.

Препратката, която държите, препраща към „стария“ обект, който е бил изтрит от операцията за преразпределяне на вектор. Използването на тази препратка ще доведе до недефинирано поведение. Същото като случая на показалеца. Вие също не можете да съхранявате итератор, те също са невалидни.

Ще трябва да намерите друг начин да следите този елемент. Как точно ще го направите зависи от това, което се опитвате да направите. Понякога просто поддържането на индекс е достатъчно.

person Mat    schedule 31.05.2014