Итак. У меня есть команды для создания и для удаления некоторого объекта из модели.
class CreateItemCommand : public QUndoCommand
{
public:
CreateItemCommand(ItemModel *model, QUndoCommand *parent = 0):
QUndoCommand(parent),
m_model(model),
m_item(new Item) {
}
void redo() {
m_model->addItem(m_item);
}
void undo() {
m_model->removeItem(m_item);
}
private:
ItemModel *const m_model;
Item *const m_item;
};
class RemoveItemCommand : public QUndoCommand
{
public:
RemoveItemCommand(ItemModel *model, Item *item, QUndoCommand *parent = 0):
QUndoCommand(parent),
m_model(model),
m_item(item) {
}
void redo() {
m_model->removeItem(m_item);
}
void undo() {
m_model->addItem(m_item);
}
private:
ItemModel *const m_model;
Item *const m_item;
};
Всё бы хорошо, но такой простой подход чреват утечками памяти. Если так получилось, что на итем не ссылаются больше никакие команды (то есть они уничтожены тем или иным способом) и он не находится в ItemModel, то он занимает лишнюю память.
Qt Undo Framework устроен так, что команды в могут удаляться в одном из следующих случаев:
- делается QUndoStack::clear();
- при отменённой команде в QUndoStack добавляется некоторая другая команда;
- в QUndoStack установлен лимит количества команд (undoLimit) и мы вышли за его пределы.
Таким образом, команды могут удаляться находясь как в отменённом, так и в применённом состоянии. И контролировать память при этом несколько затруднительно.
В случае собственной модели решение проблемы простое: нигде не храним указатели на Item напрямую. Везде используем QSharedPointer<Item> (в CreateItemCommand, в RemoveItemCommand и в ItemModel). Не надо заботиться об удалении — этот итем самоудалится, если на него больше ничего не ссылается. Тут даже деструкторы писать не надо.
Всё усложняется, если мы не контролируем то, как хранятся указатели на итемы в ItemModel. Например, если ItemModel унаследован от QGraphicsScene (а там, как известно, итемы хранятся в виде обычных указателей). Как тут быть?
P.S.: по ходу, в примере из документации Qt как раз-таки допущен косяк, из-за которого будет происходить такая утечка памяти — единожды созданные итемы ни при каких условиях не удаляются. Запощу завтра в их багзиллу.