Russian Qt Forum

Qt => Qt Quick => Тема начата: Vladimir от Январь 23, 2020, 16:37



Название: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Vladimir от Январь 23, 2020, 16:37
Доброго дня! Вопрос как правильно удалять данные из модели сделанной таким образом:
https://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html пример с QObjectList-based Model.
В плюсовой части есть список QList<QObject*> lstData; в который добавляется структура типа
Код:
class Data: public QObject
{
    Q_OBJECT

    Q_PROPERTY(...)
  
    public:
        Data() {}

       ~Data()
        {
              qDebug() << "~Data";
        }

....

};

добавляется lstData.append(new Data());
после этого делается context->setContextProperty("M_Data", QVariant::fromValue(lstData));

в QML части:
Код:
ListView
{
      id: idTableBody
      anchors { fill: parent }                
      clip: true
      model: M_Data
}
и все вроде ок, когда я редактирую/добавляю данные в модель, а потом делаю обновление как написано по ссылке context->setContextProperty("M_Data", QVariant::fromValue(lstData));
интересности начинаются, когда я пытаюсь удалить данные..
чтобы удалить одну строку делаю так
Код:
for(int i = 0; i < lstData.count(); ++i)
    {
        auto *data= dynamic_cast<Data*>(lstData[i]);
        if(data)
        {
            if(data->getNumber() == delNumberData)
            {
                lstData.removeAt(i);
                delNumberReason = -1;
                return true;
            }
        }
    }
для удаления всех строк вызываю просто lstData.clear() и это работает, НО.. я не вижу, чтобы вызывался деструктор объекта!
removeAt(); и clear() вроде как и не должны его вызывать.. ок, тогда я делаю
delete lstData.takeAt(i); для удаления одной строчки и qDeleteAll(lstData); и для удаления всего списка и при этом деструктор вызывается с....(внимание!) обязательным крашем программы! :)
и вот собственно как это разрулить счастье, кто отвечает за освобождение памяти.. и как это правильно сделать?


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: kambala от Январь 23, 2020, 18:19
может дело в том, что деструктор не объявлен виртуальным?


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Vladimir от Январь 23, 2020, 19:09
может дело в том, что деструктор не объявлен виртуальным?

это улучшило ситуацию, т.е. теперь он крашит не стабильно каждый раз, но все же крашит через какое-то количество удалений :(


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Vladimir от Январь 23, 2020, 21:17
Как-то странно.. виртуальный деструктор нужен в базовом классе, для корректного удаления производных классов через указатель на базовый. Базовым классом для моего класса Data является QObject, а у него деструктор виртуальный! Поэтому вроде как все должно работать было и без добавления virtual в мой класс Data.. или что-то я не так понимаю


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: kambala от Январь 23, 2020, 23:57
нет, все наследники должны объявлять свой деструктор виртуальным.

раз падает при удалении объекта, значит он точно удаляется дважды.


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Old от Январь 24, 2020, 07:29
Нельзя просто так взять и удалить данные в модели не уведомив при этом вьюшки.
Почитайте про:
void QAbstractItemModel::beginRemoveRows(const QModelIndex &parent, int first, int last)
void QAbstractItemModel::endRemoveRows()

Тоже касается и добавления данных в модель:
void QAbstractItemModel::beginInsertRows(const QModelIndex &parent, int first, int last)
void QAbstractItemModel::endInsertRows()

Ну и про другие случаи не забывайте, смотрите beginXXX, endXXX.


Название: Re: Правильное удаление объектов из ++ модели k
Отправлено: ssoft от Январь 24, 2020, 07:37
нет, все наследники должны объявлять свой деструктор виртуальным.

С чего бы это?

для удаления всех строк вызываю просто lstData.clear() и это работает, НО.. я не вижу, чтобы вызывался деструктор объекта!

Здесь удаляются не экземпляры объектов, а чистится массив указателей на них. Никакой деструктор вызван не будет.

delete lstData.takeAt(i); для удаления одной строчки и qDeleteAll(lstData); и для удаления всего списка и при этом деструктор вызывается с....(внимание!) обязательным крашем программы!

А здесь сначала удаляется один экземпляр объекта, а затем удаляются все экземпляры объектов массива, в том числе и уже удаленный. Ошибка попытки повторного удаления.

Используйте qDeleteAll(lstData), а уже затем lstData.clear() и будет счастье.
Ну и не забывайте про beginRemoveRows/endRemoveRows и beginResetModel/endResetModel.


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Vladimir от Январь 24, 2020, 09:33
Нельзя просто так взять и удалить данные в модели не уведомив при этом вьюшки.
Почитайте про:
void QAbstractItemModel::beginRemoveRows(const QModelIndex &parent, int first, int last)
void QAbstractItemModel::endRemoveRows()

Тоже касается и добавления данных в модель:
void QAbstractItemModel::beginInsertRows(const QModelIndex &parent, int first, int last)
void QAbstractItemModel::endInsertRows()

Ну и про другие случаи не забывайте, смотрите beginXXX, endXXX.

про эти методы я знаю, но где их взять если я не наследуюсь от QAbstractItemModel? по ссылке из доки, которую я привел в первом сообщении я использую
QObjectList-based Model, основанную просто на QList<QObject*>. если есть такой способ, то должен быть и корректный способо удаления без begin/end методов.


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Old от Январь 24, 2020, 09:38
Тогда попробуйте вначале установить новый новый список, в котором уже нет удаленных строк и только после этого удалить сами объекте.


Название: Re: Правильное удаление объектов из ++ модели k
Отправлено: Vladimir от Январь 24, 2020, 09:39
Цитировать
Используйте qDeleteAll(lstData), а уже затем lstData.clear() и будет счастье.

Именно так и делаю, но счастье не приходит, когда я писал про "delete lstData.takeAt(i); для удаления одной строчки и qDeleteAll(lstData); для удаления всего" - это разные функции удаления. когда я удаляю все я делаю сначала qDeleteAll(lstData), а потом lstData.clear(). и он рандомно крашит с виртуальным конструктором, a без виртульного он крашит стабильно


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Vladimir от Январь 24, 2020, 09:41
Тогда попробуйте вначале установить новый новый список, в котором уже нет удаленных строк и только после этого удалить сами объекте.

т.е. в context->setContextProperty("M_Data", QVariant::fromValue(QList<QObject*>())); сначала установить пустой список, а потом уже чистить.. это идея, попробую


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Old от Январь 24, 2020, 09:45
т.е. в context->setContextProperty("M_Data", QVariant::fromValue(QList<QObject*>())); сначала установить пустой список, а потом уже чистить.. это идея, попробую
Не обязательно.
Перед удалением объекта из списка через removeAt, вызвать у него deleteLater, сделать removeAt и установить новый список.


Название: Re: Правильное удаление объектов из ++ модели связанной со списком в QML [DONE]
Отправлено: Vladimir от Январь 24, 2020, 11:05
Eeeee вроде заработало. Всем спасибо! Eще потестирую.. но выглядит вроде все логично! Финальный вариант такой:
удаление одного
Код:
for(int i = 0; i < lstData.count(); ++i)
    {
        auto *data= dynamic_cast<Data*>(lstData[i]);
        if(data)
        {
            if(data->getNumber() == delNumber)
            {
                lstData[i]->deleteLater();
                lstData.removeAt(i);
                delNumber = -1;
                context->setContextProperty("M_Data", QVariant::fromValue(lstData));
                return true;
            }
        }
    }

удаление всех

Код:
context->setContextProperty("M_Data", QVariant::fromValue(QList<QObject*>()));
qDeleteAll(lstData);
lstData.clear();

остался открытый теоретический вопрос нужен ли деструктор виртульным делать в дочернем классе. вроде и без него работает..
здесь его объявляют только в базовом классе http://cpp-reference.ru/articles/virtual-destructor/


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Old от Январь 24, 2020, 11:10
остался открытый теоретический вопрос нужен ли деструктор виртульным делать в дочернем классе. вроде и без него работает..
Не нужно, если он в базовом уже виртуальный, то будет виртуальным и во всех потомках.


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: kambala от Январь 24, 2020, 11:26
нет, все наследники должны объявлять свой деструктор виртуальным.

С чего бы это?
спасибо, и правда, не нужно. почему-то запомнил, что лучше везде объявлять как виртуальный.


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Old от Январь 24, 2020, 11:54
спасибо, и правда, не нужно. почему-то запомнил, что лучше везде объявлять как виртуальный.
По мне, так лучше везде и объявлять, что бы не приходилось помнить/смотреть как там в базовых классах определен деструктор. :)


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: ViTech от Январь 24, 2020, 12:27
спасибо, и правда, не нужно. почему-то запомнил, что лучше везде объявлять как виртуальный.
По мне, так лучше везде и объявлять, что бы не приходилось помнить/смотреть как там в базовых классах определен деструктор. :)

Как по мне, так в дочерних классах лучше всё же override (https://en.cppreference.com/w/cpp/language/override) использовать :).


Название: Re: Правильное удаление объектов из ++ модели ассоциированной со списком в QML
Отправлено: Old от Январь 24, 2020, 13:07
спасибо, и правда, не нужно. почему-то запомнил, что лучше везде объявлять как виртуальный.
По мне, так лучше везде и объявлять, что бы не приходилось помнить/смотреть как там в базовых классах определен деструктор. :)

Как по мне, так в дочерних классах лучше всё же override (https://en.cppreference.com/w/cpp/language/override) использовать :).
А я и то и то использую. :)