#c #pointers
#c #указатели
Вопрос:
У меня есть следующие классы:
class A {};
class B { vector<A> vect; };
Я могу получить доступ к произвольному A следующим образом:
A a = b.vect[0];
// A *a_ptr = amp;a;
Но как я могу напрямую перейти к *a_ptr?
A *a_ptr = amp;b.vet[0];
компилируется и не выдает ошибок во время выполнения, но указывает на неправильное расположение в памяти.
Редактировать:
мой реальный пример:http://ideone.com/kP8NK
В то время как Ideone выдает ожидаемое « You are now at 0, 0, 0
«, компилятор MS VisualStudio выдает « You are now at 6624656, -33686019, -1414812757
«
Комментарии:
1. Что вы подразумеваете под «он указывает на неправильное местоположение в памяти»?, Кстати, делать это опасно, так как следующий
push_back
может сделать этот указатель недействительным.2. получение значения a_ptr.Field дает мне неправильное значение, в то время как значение.Field является правильным
3. вам пришлось бы выполнить a_ptr -> Поле
4. @pistacchio: это невозможно, поскольку
A a = b.vect[0]
копирует объект из того же местоположения, откуда вы берете указатель.5. почему вы не используете ссылки, это то, что в любом случае возвращает
operator[]
of vector.
Ответ №1:
Это не «неправильно», вы просто упустили разницу. А именно:
A a = b.vect[0]; // Makes `a` a *copy* of the vector element
A *a_ptr = amp;a; // Address of the copy
A *a_ptr2 = amp;b.vect[0]; // Address of the element, not a copy
Чтобы получить эквивалентность, вы должны изменить свой первый на:
Aamp; a = b.vect[0]; // Makes `a` a reference to the vector element
A *a_ptr = amp;a; // Address of the element, not a copy
Если вы все еще не получаете то, что ожидаете, после наблюдения за этим различием, тогда вам нужно показать нам ваш точный пример вместе с тем, как вы определяете, каковы «правильные» и «неправильные» результаты.
Проблема в том, что std::vector<>::push_back
это приведет к аннулированию ссылок, указателей и итераторов на элементы, когда size() == capacity()
поскольку ему необходимо выделить новый фрагмент памяти. Использование недействительного указателя (и др.) приводит к неопределенному поведению, поэтому ваши результаты неизвестны.
Вместо этого вы должны сохранить индексы и выполнить тривиальный поиск, чтобы получить фактический элемент. (Примечание vector
остается упорядоченным, поэтому, даже если ваши номера могут перемещаться в памяти, их индекс остается тем же.)
Комментарии:
1. Я не думаю, что это имеет смысл, потому что vector возвращает ссылку, а не копию object
2. @Oleg: Да, он возвращает ссылку, но
a
это не ссылочный тип, это тип значения; поэтому для его инициализации создается копия (ссылки).3. верно, но в чем разница между получением адреса из ссылки, возвращаемой методом, или инициализацией возвращаемым значением?
4. @Oleg: Они будут другими. (Однако теперь мы знаем, что у операционной системы возникли проблемы не с этой проблемой.)
Ответ №2:
A *a_ptr = amp;b.vect[0];
правильно. Если у вас это не работает, значит, вы делаете что-то другое или модифицируете вектор после получения указателя. Модификации вектора обычно делают недействительными все указатели и итераторы на его элементах.
Ответ №3:
У меня это работает (после изменения vet
на vect
). Смотрите http://ideone.com/V3tGS для доказательства.
Комментарии:
1.
class A { public: ... };
? Фу. 🙂struct A { ... };
.2. это была опечатка, поскольку опубликованный пример является упрощением реального кода, который вы теперь можете увидеть при РЕДАКТИРОВАНИИ
3. @GMan: Я хотел внести как можно меньше изменений в код OP. В противном случае мы получаем ответный вопрос: «Почему вы изменили
class
наstruct
?»
Ответ №4:
Ваша проблема в том, что вы изменяете вектор после присвоения адреса указателю. Здесь:
World w;
w.worldMap.push_back(Room(0, 0, 0)); // starting point
Room *starting_room = amp;w.worldMap[0];
w.worldMap.push_back(Room(1, 1, 5)); // treasure room
Room treasure_room = w.worldMap[1];
Изменение контейнера делает недействительными все указатели / ссылки.
Если вы удалите эти строки:
w.worldMap.push_back(Room(1, 1, 5)); // treasure room
Room treasure_room = w.worldMap[1];
Это работает идеально