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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Удаление объекта из списков QList, QSet...при вызове деструктора объекта  (Прочитано 10110 раз)
ltise
Гость
« : Август 12, 2010, 12:24 »

Привет всем!

Чета заморочился, не могу найти подходящего решения...

Есть некий объект Т (от QObject), УКАЗАТЕЛЬ на объект одновременно находится в разных списках QList, QSet и т.п.

необходимо при удалении объекта, удалять указатель на него из всех списков, автоматически.

я решил использовать сигнал destroyed(QObject*object), и создать свои классы List, Set, где при добавлении подписывать список на сигнал объекта destroyed(....) типа:

Код:
template <typename T>
class List : public QList<T>
{
public:
T add(T item){
append(item);
                connect(item, SIGNAL(destroyed(QObject*), this, SLOT(_destroyed(QObject*)));
}
private slot:
        void _destroyed(QObject*object){
                removeOne(object); // как-то так....
        }
};

Но! Qt пишет, что не может использовать Q_OBJECT в классах-шаблонах...
что делать, может есть другое решение ?
« Последнее редактирование: Август 12, 2010, 13:09 от ltise » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Август 12, 2010, 12:51 »

Есть смысл хранить в QList не сами объекты, а указатели на них. Напр. есть 2 или более QList. с указателями на одни и те же объекты. Тогда нормально удалять указатели из того или иного списка. А если бы Вам даже и удалось хранить сами объекты - то это были бы копии/'экземпляры объектов (никак не связанные друг с другом) т.е. "разные объекты" а не "те же в разных списках"
Записан
ltise
Гость
« Ответ #2 : Август 12, 2010, 13:05 »

Есть смысл хранить в QList не сами объекты, а указатели на них. Напр. есть 2 или более QList. с указателями на одни и те же объекты. Тогда нормально удалять указатели из того или иного списка. А если бы Вам даже и удалось хранить сами объекты - то это были бы копии/'экземпляры объектов (никак не связанные друг с другом) т.е. "разные объекты" а не "те же в разных списках"

Сорри, я имел ввиду указатели:

Код:
List<MyPoint*> list;

MyPoint * p1 = new MyPoint();
MyPoint * p2 = new MyPoint();
MyPoint * p3 = new MyPoint();

list.add(p1);
list.add(p2);
list.add(p3);

qDebug() << "size: " << list.size();  // size: 3

delete p2;

qDebug() << "size: " << list.size();  // ДОЛЖНО БЫТЬ size: 2, сейчас возвращает size: 3



« Последнее редактирование: Август 12, 2010, 16:32 от ltise » Записан
crossly
Гость
« Ответ #3 : Август 12, 2010, 13:53 »

ну так если указатель один... в разных списках... то если удалить указатель то перестанет существовать во всех...
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Август 12, 2010, 15:55 »

Ну и делайте QList указателей
Код:
template <class T>
class List : public QList<T *>
{
public:
void add(T * item) {
append(item);
                connect(item, SIGNAL(destroyed(QObject*), this, SLOT(_destroyed(QObject*)));
}
private slot:
        void _destroyed(QObject*object) {
                removeOne((T *) object);
        }
};
Записан
ltise
Гость
« Ответ #5 : Август 12, 2010, 16:07 »

Ну и делайте QList указателей

Заморочка в том, что не могу использовать connect(...), потому-что если вставляю в класс Q_OBJECT компилятор посылает:    Error: Template classes not supported by Q_OBJECT
Записан
ltise
Гость
« Ответ #6 : Август 12, 2010, 16:18 »

ну так если указатель один... в разных списках... то если удалить указатель то перестанет существовать во всех...
Код:
QList<MyObject *> list;
MyObject * obj1 = new MyObject();

list.append(obj1); // list.size = 1

delete obj1;
// list.size = 1
// что  останется в list ? указатель на мусор...
Записан
whirlwind
Гость
« Ответ #7 : Август 12, 2010, 17:32 »

Но! Qt пишет, что не может использовать Q_OBJECT в классах-шаблонах...
что делать, может есть другое решение ?
QList не является наследником QObject, поэтому в нем не может быть слотов и сигналов

попробуйте просто создать класс, который будет жить вместе со списком и обновлять его при необходимости

типа

Код:
class DestroyProcessor : public QObject
{
    Q_OBJECT
public:
   void SetList(QList<QObject*>* list)
   {
       m_list = list ;
   }

public slots: 
    void ProcessDestroyed ( QObject * obj = 0 )
    {
        if (m_list)
        {
            m_list->removeOne(object);
        }
    }

protected:

    QList<QObject*>*  m_list ;
}

template <class T>
class List : public QList<T *>
{
public:
    List()
    {
        m_destroyProcessor = new DestroyProcessor() ;
        m_destroyProcessor->setList(this) ;
    }
 

    void add(T * item)
    {
append(item);
        connect(item, SIGNAL(destroyed(QObject*), m_destroyProcessor, SLOT(ProcessDestroyed(QObject*)));
    }
private :

    DestroyProcessor* m_destroyProcessor ;
};

не уверен, что оно скомпилится, просто для демонстрации идеи
Записан
ltise
Гость
« Ответ #8 : Август 12, 2010, 18:02 »

Но! Qt пишет, что не может использовать Q_OBJECT в классах-шаблонах...
что делать, может есть другое решение ?
QList не является наследником QObject, поэтому в нем не может быть слотов и сигналов

попробуйте просто создать класс, который будет жить вместе со списком и обновлять его при необходимости

типа

Код:
class DestroyProcessor : public QObject
{
    Q_OBJECT
public:
   void SetList(QList<QObject*>* list)
   {
       m_list = list ;
   }

public slots: 
    void ProcessDestroyed ( QObject * obj = 0 )
    {
        if (m_list)
        {
            m_list->removeOne(object);
        }
    }

protected:

    QList<QObject*>*  m_list ;
}

template <class T>
class List : public QList<T *>
{
public:
    List()
    {
        m_destroyProcessor = new DestroyProcessor() ;
        m_destroyProcessor->setList(this) ;
    }
 

    void add(T * item)
    {
append(item);
        connect(item, SIGNAL(destroyed(QObject*), m_destroyProcessor, SLOT(ProcessDestroyed(QObject*)));
    }
private :

    DestroyProcessor* m_destroyProcessor ;
};

не уверен, что оно скомпилится, просто для демонстрации идеи

Уже пробовал.. тут затык в передаче параметра в setList..

void setList(QList<QObject*>* list)

типа:
 error: no matching function for call to 'Test::setList(QList<MyObject*>*)'



Записан
Marat(Qt)
Гость
« Ответ #9 : Август 12, 2010, 20:22 »

А зачем делать QList<MyObject*>, если MyObject все равно от QObject наследуется, можно делать QList<QObject*> и использовать cast'ы. не айс конечно, но на безрыбье..
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Август 12, 2010, 22:08 »

Мда, идея нормальная/естественная, а вот получается как-то сложновато. Замечание что QList не может принять сигнал абсолютно верно.  Может лучше пусть объект хранит в себе все указатели на QList в которые он входит и в деструкторе вычеркивает себя из всех списков?
Записан
ltise
Гость
« Ответ #11 : Август 12, 2010, 22:49 »

Мда, идея нормальная/естественная, а вот получается как-то сложновато. Замечание что QList не может принять сигнал абсолютно верно.  Может лучше пусть объект хранит в себе все указатели на QList в которые он входит и в деструкторе вычеркивает себя из всех списков?
Тут тоже грабли везде.. объект должен знать о удалении списка, о удалении себя из списка..
надо попробовать...
Записан
ltise
Гость
« Ответ #12 : Август 13, 2010, 00:04 »

Вобщем через свой листенер получилось:

Код:

class MyObject;

// абстрактный класс слушателя события удаления объекта
class DestroyListener
{
public:
DestroyListener() {}

virtual void destroy(MyObject*object)=0;
};

// статический класс принимает и рассылает уведомления
class ObjectsManager
{
public:
static inline void registry(MyObject*object, DestroyListener*listener)
{
_listeners.insertMulti(object, listener);
}

static inline void destroy(MyObject * object)
{
QList<DestroyListener*> listeners = _listeners.values(object);
_listeners.remove(object);

                // оповещаем о удалении всех зарегистрированных слушателей
foreach(DestroyListener*listener, listeners){
listener->destroy(object);
}
}

private:
static QMultiMap<MyObject*, DestroyListener*> _listeners;
};

QMultiMap<MyObject*, DestroyListener*> ObjectsManager::_listeners;

// базовый класс
class MyObject
{

public:
MyObject() {}

virtual ~MyObject()
{
                // сообщаем о удалении
ObjectsManager::destroy(this);
}

};



template <typename T>
class List : public QList<T>, DestroyListener
{
public:
T add(T item){
QList<T>::append(item);

                // подписываемся на получения событий удаления
ObjectsManager::registry(item, this);
return item;
}

void destroy(MyObject*object)
{
                // реализуем реакцию на уничтожение объекта, т.е. выносим его из списка
T item = static_cast<T>(object);
removeOne(item);
}

};

// тестовый класс
class TestObject : public MyObject
{
public:
TestObject(int i) : MyObject(),
_i(i)
{
}

inline int getI(){
return _i;
}

private:
int _i;
};


ТЕСТ:

Код:
	QTime timer;
timer.start();

List<TestObject*> list;
int n = 100000;

for(int i=0; i < n; i++){
TestObject * t = new TestObject(1);
list.add(t);
}

qDebug() << timer.elapsed() << list.size();

TestObject * t = list.at(n/2);

delete t;

qDebug() << timer.elapsed() << list.size();
« Последнее редактирование: Август 13, 2010, 00:06 от ltise » Записан
SABROG
Гость
« Ответ #13 : Август 13, 2010, 00:51 »

Можно было сделать через QWeakPointer. Его всегда можно проверить на валидность, если речь идет об указателях на QObject'ы, и удалять не валидный, если он не нужен в списке.
Записан
ltise
Гость
« Ответ #14 : Август 13, 2010, 06:33 »

Можно было сделать через QWeakPointer. Его всегда можно проверить на валидность, если речь идет об указателях на QObject'ы, и удалять не валидный, если он не нужен в списке.
Тут сразу встает вопрос, куда воткнуть проверку на валидность ?
В общем случае напрягает обращение через QWeakPointer::data()....
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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