Может ли QPointer быть ключом к std::map
Согласно документу SGI об ассоциативных контейнерах, "поскольку элементы хранятся в соответствии с их ключами, важно, чтобы ключ, связанный с каждым элементом, был неизменным". Иногда я использую указатель в качестве ключа для std:: map, поскольку, хотя указанный объект может быть изменяемым, сам указатель является постоянным.
Технически QPointer - это объект, имитирующий указатель, и документация Qt говорит, что мы можем использовать QPointers точно так же, как указатели. Поскольку сам объект QPointer может измениться во время выполнения, может ли он по-прежнему использоваться в качестве ключа для контейнера std:: map?
Редактировать 1: я не могу использовать QMap, я должен придерживаться std::map.
Редактировать 2: код компилируется, когда я использую QPointer. Вопрос в том, стоит ли ожидать неприятных сюрпризов во время выполнения.
3 ответа
Нет, это не безопасно, потому что QPointer<T>
может измениться на NULL
когда QObject
уничтожен (QPointer
похож на std::weak_ptr
.) Таким образом, этот код будет производить неопределенное поведение:
class CopyableWidget : public QWidget {
Q_OBJECT;
public:
CopyableWidget(Widget* parent = 0) : QWidget(parent) {}
CopyableWidget(const CopyableWidget& w, Widget* parent = 0) : QWidget(parent) {}
};
std::vector<CopyableWidget> v(2); // Two default-constructed widgets
std::map<QPointer<CopyableWidget>, int> wid_values;
wid_values[&v[0]] = 1;
wid_values[&v[1]] = 2;
// wid_values contains { {&v[0], 1}, {&v[1], 2} }
v.resize(1);
// The QPointer in wid_values constructed from &v[1] now acts like NULL.
// wid_values contains { {&v[0], 1}, {NULL, 2} }
// But &v[0] > NULL (on most platforms). Class invariant was violated!
Это неопределенное поведение - сравнивать указатели из разных блоков выделения для чего-либо, кроме равенства / неравенства. Т.е. после выделения блока с помощью new или malloc, вы можете пройти через буфер и проверить, меньше ли ваш указатель, чем конец буфера. Но вы не можете сделать две отдельные новости / malloc и сравнить полученные указатели для чего-либо, кроме равенства / неравенства.
Ну... вы можете, но вам могут не понравиться результаты.
Я понимаю (из принципа наименьшего удивления), что QPointer
не будет "волшебным образом" менять то, куда он указывает, если вы не переназначаете его и не удаляете базовый объект, поэтому он должен быть безопасным.
(Должен добавить, что я видел, как Qt несколько раз нарушал принцип наименьшего удивления, так что проверяйте, чтобы быть уверенным.)