Russian Qt Forum
Ноябрь 25, 2024, 17:17 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Правильное удаление объектов из ++ модели ассоциированной со списком в QML  (Прочитано 8547 раз)
Vladimir
Крякер
****
Offline Offline

Сообщений: 305



Просмотр профиля
« : Январь 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); и для удаления всего списка и при этом деструктор вызывается с....(внимание!) обязательным крашем программы! Улыбающийся
и вот собственно как это разрулить счастье, кто отвечает за освобождение памяти.. и как это правильно сделать?
« Последнее редактирование: Январь 23, 2020, 21:33 от Vladimir » Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #1 : Январь 23, 2020, 18:19 »

может дело в том, что деструктор не объявлен виртуальным?
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Vladimir
Крякер
****
Offline Offline

Сообщений: 305



Просмотр профиля
« Ответ #2 : Январь 23, 2020, 19:09 »

может дело в том, что деструктор не объявлен виртуальным?

это улучшило ситуацию, т.е. теперь он крашит не стабильно каждый раз, но все же крашит через какое-то количество удалений Грустный
Записан
Vladimir
Крякер
****
Offline Offline

Сообщений: 305



Просмотр профиля
« Ответ #3 : Январь 23, 2020, 21:17 »

Как-то странно.. виртуальный деструктор нужен в базовом классе, для корректного удаления производных классов через указатель на базовый. Базовым классом для моего класса Data является QObject, а у него деструктор виртуальный! Поэтому вроде как все должно работать было и без добавления virtual в мой класс Data.. или что-то я не так понимаю
Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #4 : Январь 23, 2020, 23:57 »

нет, все наследники должны объявлять свой деструктор виртуальным.

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

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #5 : Январь 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.
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #6 : Январь 24, 2020, 07:37 »

нет, все наследники должны объявлять свой деструктор виртуальным.

С чего бы это?

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

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

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

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

Используйте qDeleteAll(lstData), а уже затем lstData.clear() и будет счастье.
Ну и не забывайте про beginRemoveRows/endRemoveRows и beginResetModel/endResetModel.
« Последнее редактирование: Январь 24, 2020, 07:39 от ssoft » Записан
Vladimir
Крякер
****
Offline Offline

Сообщений: 305



Просмотр профиля
« Ответ #7 : Январь 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 методов.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #8 : Январь 24, 2020, 09:38 »

Тогда попробуйте вначале установить новый новый список, в котором уже нет удаленных строк и только после этого удалить сами объекте.
Записан
Vladimir
Крякер
****
Offline Offline

Сообщений: 305



Просмотр профиля
« Ответ #9 : Январь 24, 2020, 09:39 »

Цитировать
Используйте qDeleteAll(lstData), а уже затем lstData.clear() и будет счастье.

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

Сообщений: 305



Просмотр профиля
« Ответ #10 : Январь 24, 2020, 09:41 »

Тогда попробуйте вначале установить новый новый список, в котором уже нет удаленных строк и только после этого удалить сами объекте.

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

Сообщений: 4350



Просмотр профиля
« Ответ #11 : Январь 24, 2020, 09:45 »

т.е. в context->setContextProperty("M_Data", QVariant::fromValue(QList<QObject*>())); сначала установить пустой список, а потом уже чистить.. это идея, попробую
Не обязательно.
Перед удалением объекта из списка через removeAt, вызвать у него deleteLater, сделать removeAt и установить новый список.
Записан
Vladimir
Крякер
****
Offline Offline

Сообщений: 305



Просмотр профиля
« Ответ #12 : Январь 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/
« Последнее редактирование: Январь 24, 2020, 11:15 от Vladimir » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #13 : Январь 24, 2020, 11:10 »

остался открытый теоретический вопрос нужен ли деструктор виртульным делать в дочернем классе. вроде и без него работает..
Не нужно, если он в базовом уже виртуальный, то будет виртуальным и во всех потомках.
Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #14 : Январь 24, 2020, 11:26 »

нет, все наследники должны объявлять свой деструктор виртуальным.

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

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.173 секунд. Запросов: 23.