Я смущен тем, как С++ управляет объектами в векторе. Скажем, я делаю следующее:
Является ли ссылка c все еще действительной (или, еще лучше, гарантировано ли она действительной)? Если нет, я должен всегда делать копию из ссылки для обеспечения безопасности?
Ответ 2
В отличие от ссылок на Java или С# (которые больше похожи на указатели С++, чем ссылки С++), ссылки на С++ являются "тупыми" как указатели, что означает, что если вы получаете ссылку на объект, а затем вы перемещаете этот объект в память, ваша ссылка больше не действительна.
Является ли ссылка c все еще действительной (или, еще лучше, гарантирована ли она)?
В случае, когда вы описываете, стандартным векторам не гарантируется сохранение объектов, которые он содержит в одном и том же месте в памяти при изменении содержимого вектора (удаление элемента, изменение размера вектора и т.д.).
Это приведет к аннулированию как итераторов, так и указателей/ссылок на содержащийся объект.
Если нет, мне нужно всегда делать копию из ссылки для обеспечения безопасности?
Существует несколько способов продолжить "указывать" на нужные объекты, все из которых подразумевают уровень косвенности.
Копия полного/значения
Самый простой способ сделать полную копию MyClass:
vector<MyClass> x ;
x.push_back(a) ;
x.push_back(b) ;
MyClass c = x[1] ; // c is a full copy of b, not a reference to b
x.erase(x.begin()) ;
Используя правый контейнер
Вторым простейшим является использование std::list
, специально разработанного для вставки и удаления элементов, и не будет изменять содержащиеся в нем объекты, а также не указывать на них указатели, ссылки или итераторы:
list<MyClass> x ;
x.push_back(a) ;
x.push_back(b) ;
list<MyClass> it = x.begin() ;
++it ;
MyClass & c = *it ;
x.erase(x.begin()) ;
Использование указателей (небезопасно)
Другим было бы сделать std::vector<MyClass *>
, который будет содержать указатели на MyClass
вместо MyClass
объектов. Затем вы сможете сохранить указатель или ссылку на заостренный объект с немного другой записью (из-за дополнительной косвенности):
vector<MyClass *> x;
x.push_back(a); // a being a MyClass *
x.push_back(b); // b being a MyClass *
MyClass * c = x[1]; // c points to the same object as b
x.erase(x.begin()); // note that a will still need separate deallocation
Это небезопасно, потому что нет ясного (насколько это касается компилятора) владельца объектов a и b, то есть отсутствует четкая часть кода, ответственная за освобождение от них, когда они больше не нужны (это как утечки памяти происходят в C и С++)
Итак, если вы используете этот метод, убедитесь, что код хорошо инкапсулирован и как можно меньше, чтобы избежать сюрпризов обслуживания.
Использование интеллектуальных указателей (безопаснее)
Что-то лучше будет использовать умные указатели. Например, используя С++ 11 (или boost) shared_ptr
:
vector< shared_ptr<MyClass> > x;
x.push_back(a); // a being a shared_ptr<MyClass>
x.push_back(b); // b being a shared_ptr<MyClass>
shared_ptr<MyClass> c = x[1]; // c points to the same object as b
x.erase(x.begin()); // No deallocation problem
Теперь, если вы используете shared_ptr
и ничего не знаете о weak_ptr
, у вас есть проблема, поэтому вы должны закрыть этот пробел.
Использование интеллектуальных указателей 2 (безопаснее)
Другим решением было бы использовать С++ 11 unique_ptr
, который является эксклюзивным владельцем заостренного объекта. Поэтому, если вы хотите иметь указатель или ссылку на объект-указатель, вам придется использовать необработанные указатели:
vector< unique_ptr<MyClass> > x;
x.push_back(a); // a being a unique_ptr<MyClass>
x.push_back(b); // b being a unique_ptr<MyClass>
MyClass * c = x[1].get(); // c points to the same object as b
x.erase(x.begin()); // No deallocation problem
Обратите внимание, что вектор является единственным владельцем объектов, в отличие от вышеприведенного случая с smart_ptr
.
Заключение
Вы кодируете на С++, то есть вы должны выбрать правильный метод для своей проблемы.
Но сначала вы должны быть уверены, что понимаете уровень косвенности, добавленный указателями, какие указатели и какие ссылки на С++ (и почему они не являются ссылками на С#/Java).