Q_FOREACH deleting objects
Hello ,
This is an interesting problem. I iterate over a list of children() (defined in QObject). Initially there are 7 children. Then iterating over the contianer, I check for a particular object. If found I delete it.
Now the problem:::
1) Start Iterator with count as 7
2) Found object at position 4th in the container.
3) The object matches and I delete it.
4) Now the count of the container is 6.
5) Now according to me the container should resize automatically and should shift the objects. But it doesnt happen and my application crashes.
Any idea Why????
Code:
Q_FOREACH( QObject *obj, children
() ) {
if( obj->isA( "myObject" ) )
(qobject_cast<myObject *>(obj))->deleteSelected();
}
Thanks,
Veda.
Re: Q_FOREACH deleting objects
I suggest you use deleteLater() and forget about the problem ;)
And seriously, I assume children() returns a QList<QObject*>. If you delete an object pointed by a pointer, you don't delete the pointer itself, therefore it is still a valid "object" and occupies space in the list. The list is not "smart" in any way and doesn't know or care what you keep in your pointers.
Re: Q_FOREACH deleting objects
Q_FOREACH and foreach use implicit sharing (copy-on-write). If you modify the container, a copy will be made, and the iteration will continue in the unmodified copy.
Re: Q_FOREACH deleting objects
Quote:
Originally Posted by
Brandybuck
Q_FOREACH and foreach use implicit sharing (copy-on-write).
Actually this is not true. Implicit sharing indeed is used by foreach() here but it is only because Qt containers are implicitely shared. If you pass a non-shared container to foreach, it'll get deep copied.
Re: Q_FOREACH deleting objects
Quote:
Originally Posted by
wysota
Actually this is not true. Implicit sharing indeed is used by foreach() here but it is only because Qt containers are implicitely shared.
That's what I meant.
Re: Q_FOREACH deleting objects
Quote:
I suggest you use deleteLater() and forget about the problem ;)
According to QtAssistence--> Defination of deleteLater()
"Schedules this object for deletion.
The object will be deleted when control returns to the event loop."
Is it possible to return the control to the event loop immediately after the object is deleted. Or in other words how do I return the control such that my object gets deleted immediately.
Also, since children() gets updated immediately after any object is deleted, I think pointer to an object should also get deleted. I have written a sample program where the deletion happens without any problem.
Code:
class myObject;
public:
~mainObject(){}
void deleteTheObject();
};
class myObject : public mainObject{
public:
myObject
( QObject * parent
= 0 ) : mainObject
( parent
){} ~myObject(){}
void deleteSelected(){
delete this;
}
private:
};
void mainObject::deleteTheObject(){
Q_FOREACH( QObject* obj, children
() ){ qDebug() << children().count();
if( obj->objectName() == "c" ){
((myObject*)obj)->deleteSelected();
}
}
}
Uses
Code:
int main(int argc, char *argv[]){
mainObject* ob = new mainObject;
myObject* a = new myObject(ob);
a->setObjectName( "a" );
myObject* b = new myObject(ob);
b->setObjectName( "b" );
myObject* c = new myObject(ob);
c->setObjectName( "c" );
myObject* d = new myObject(ob);
d->setObjectName( "d" );
myObject* e = new myObject(ob);
e->setObjectName( "e" );
ob->deleteTheObject();
return 0;
}
In the above program, after the object is deleted, children() gets updated, the count is decreased.
I tried modifying the above program with below given statement, But was unsuccessful.
Quote:
If you delete an object pointed by a pointer, you don't delete the pointer itself, therefore it is still a valid "object" and occupies space in the list.
How do i delete the pointer to an object in QList<QObject*>????
Wysota, can you please clear my funda.
One more small doubt.
In qt3 children() is a typedef for QList<QObject> and in Qt4 it is QList<QObject*>. Can you please clear me about this. what happens when object is deleted in both cases. I read Assistance, but there is still confusion.
Thanks
Re: Q_FOREACH deleting objects
Quote:
Originally Posted by
vermarajeev
According to QtAssistence--> Defination of deleteLater()
"Schedules this object for deletion.
The object will be deleted when control returns to the event loop."
Is it possible to return the control to the event loop immediately after the object is deleted. Or in other words how do I return the control such that my object gets deleted immediately.
Don't worry about that. Once you return from the slot all items scheduled for deletion will be deleted. I'd call it an "immediate" action.
Quote:
Also, since children() gets updated immediately after any object is deleted, I think pointer to an object should also get deleted.
No, of course not. You have a list of "objects" where the "object" is a pointer. And that "object" will remain there as long as you don't remove it from the list, no matter what it holds.
Quote:
I have written a sample program where the deletion happens without any problem.
Code:
class myObject;
public:
~mainObject(){}
void deleteTheObject();
};
class myObject : public mainObject{
public:
myObject
( QObject * parent
= 0 ) : mainObject
( parent
){} ~myObject(){}
void deleteSelected(){
delete this;
}
private:
};
void mainObject::deleteTheObject(){
Q_FOREACH( QObject* obj, children
() ){ qDebug() << children().count();
if( obj->objectName() == "c" ){
((myObject*)obj)->deleteSelected();
}
}
}
Uses
Code:
int main(int argc, char *argv[]){
mainObject* ob = new mainObject;
myObject* a = new myObject(ob);
a->setObjectName( "a" );
myObject* b = new myObject(ob);
b->setObjectName( "b" );
myObject* c = new myObject(ob);
c->setObjectName( "c" );
myObject* d = new myObject(ob);
d->setObjectName( "d" );
myObject* e = new myObject(ob);
e->setObjectName( "e" );
ob->deleteTheObject();
return 0;
}
In the above program, after the object is deleted, children() gets updated, the count is decreased.
Because children() returns a new list and you're iterating over the old one as Brandybuck already said.
Quote:
How do i delete the pointer to an object in QList<QObject*>????
Remove it from the list. Of course you can't do it in foreach() as you don't have access to the list, so basically what you want is not possible to do using foreach(). You should use iterators instead.
Quote:
One more small doubt.
In qt3 children() is a typedef for QList<QObject> and in Qt4 it is QList<QObject*>.
No. In Qt3 it's a typedef for QPtrList<QObject> which is exactly the same as QList<QObject*> in Qt4.
Quote:
Can you please clear me about this. what happens when object is deleted in both cases. I read Assistance, but there is still confusion.
Nothing. The object pointed gets deleted and that's it. You're left with a dangling pointer in the list.
Re: Q_FOREACH deleting objects
Hello guys,
The below given is a code in Qt3
Code:
QObjectList *list;
QObjectListIt *it;
if( (list = (QObjectList *)children()) != 0 )
{
it = new QObjectListIt( *list );
// for each found object...
while ( (obj = it->current()) != 0 )
{
++(*it);
if( obj->isA( "myObject" ) )
((myObject *)obj)->deleteSelected();
}
delete it;
}
Code:
deleteSelected()
{
delete this;
}
I tried to port it using Q_FOREACH... as discussed above using Q_FOREACH is not a good idea...
so how can i achieve the same functionality in Qt4
:confused:
Veda
Re: Q_FOREACH deleting objects
As I said, use iterators of the list to manipulate the list directly. That's exactly what the above Qt3 code does.
Re: Q_FOREACH deleting objects
Quote:
Originally Posted by
wysota
As I said, use iterators of the list to manipulate the list directly. That's exactly what the above Qt3 code does.
I just did it but again there is a dangling pointer and the application crashes. Any idea???
Code:
QObjectList *list;
QListIterator<QObject*> *it;
list = new QObjectList( children() );
if( list->count() != 0 )
{
it = new QListIterator<QObject*>( *list );
while ( it->hasNext() )
{
obj = it->next();
if( obj->isA( "myObject" ) )
((myObject *)obj)->deleteSelected();
}
delete it;
}
delete list;
Re: Q_FOREACH deleting objects
Code:
QObjectList list = children();
for(QList<QObject*>::iterator it = list.begin(); it!=list.end();++it){
if(*it && (*it)->isA("myObject")){
delete (*it);
(*it) = 0;
}
}
Of course it'd be quicker to do:
Code:
qDeleteAll(qFindChildren<myObject*>(this));
and with a bit of luck it wouldn't crash :)
Re: Q_FOREACH deleting objects
Sorry, I tried the above code too but the result is same.
Code:
QObjectList list = children();
for(QList<QObject*>::iterator it = list.begin(); it!=list.end(); ++it)
{
qDebug() << children().count();
qDebug() << list.count(); //the list( with 7 children ) is
//always same even after deleting the childrens but
//children() shows me correct count ie 6 (after deleting one object)
// but list is still 7. Meaning there is still dangling pointer.
if(*it && (*it)->isA( "myObject" ))
{
((myObject *)(*it))->deleteSelected();
if( ((myObject*)(*it))->isSelected() )
(*it) = 0;
}
}
I think the pointer is still existing and the object is deleted. But it works fine with Qt3 code. Please help...
Re: Q_FOREACH deleting objects
Why can't you understand that children() returns a new (updated) list each time you call it?
Number of children is updated every time you delete a child, so children() returns a list with an updated count, but the old list is not updated so it still has a count of 7.
There is no dangling pointer because the pointer in the list is set to 0 in line #14 and it is checked for being 0 in line #10. If it doesn't work, it means the error is elsewhere.
Please try using qFindChildren as suggested. It has a good chance to work without you having to bother about the list at all.
Re: Q_FOREACH deleting objects
Sorry, if I mistook you..
Ok let me explain what is happening.
Code:
____________________|______________________________________
| | | | | |
object1 object2 object3 object4 object5 object6
| \
object7 \
--> This object matches and I delete this
object7 --> Since this is a child of object5. object7 also gets deleted after object5
Explanation:::
Now QObject has seven children(), I iterate over the list. 'object5' matches my criteria so I delete 'object5'. Since 'object5' has one child 'object7' (which is also a child of 'QObject'), 'object7' too gets deleted along with 'object5'.
So what has happened now, both 'object5'(parent) and 'object7'( child of object5 )are deleted. When the iterator from 'QObject' points to last child (object7), tries to access an object which is already deleted (object7). Hence the program crashes.
I think now I'm clear. I also tried calling deleteLater on the object to be deleted but then object doesnt really get deleted (as it schedules to delete) and hence doesnt satisfy my application criteria, so crashes again. Hence I cannot use deleteLater either.
I dont understand, as the same functionality works fine with Qt3.
:confused:
Veda
Re: Q_FOREACH deleting objects
use deleteLater() and call QCoreApplication::processEvents() afterwards.
Re: Q_FOREACH deleting objects
Quote:
Explanation:::
Now QObject has seven children(), I iterate over the list. 'object5' matches my criteria so I delete 'object5'. Since 'object5' has one child 'object7' (which is also a child of 'QObject'), 'object7' too gets deleted along with 'object5'.
So what has happened now, both 'object5'(parent) and 'object7'( child of object5 )are deleted. When the iterator from 'QObject' points to last child (object7), tries to access an object which is already deleted (object7). Hence the program crashes.
What about using a QPointer. Assign your objects to QPointer, once the object is deleted it makes the pointer NULL. Might help...
Re: Q_FOREACH deleting objects
Yes, but you have to construct your own list then. And if you do, there is no point in using QPointer anymore as you can make sure children get deleted before parents.
Re: Q_FOREACH deleting objects
Quote:
Originally Posted by
wysota
Yes, but you have to construct your own list then. And if you do, there is no point in using QPointer anymore as you can make sure children get deleted before parents.
But OP says "Hence I cannot use deleteLater either."
I too have a doubt about using deleteLater, what if I have defined my own deleteFunction where I perform some functionality about destruction. In that case if you use deleteLater my deleteFunction wont be called and there will definately be many crashes.
I need this information coz I too have my application to port to Qt4. So I would like to know what changes to be made so that I'm safe.
Will it really effect my fuctionality changing QPtrList<QObject>(Qt3) to QList<QObject*>(Qt4)
Re: Q_FOREACH deleting objects
Quote:
Originally Posted by
vermarajeev
But OP says "Hence I cannot use deleteLater either."
Sure he can. It's enough to use processEvents() just after iterating the list.
Quote:
I too have a doubt about using deleteLater, what if I have defined my own deleteFunction where I perform some functionality about destruction. In that case if you use deleteLater my deleteFunction wont be called and there will definately be many crashes.
It's your fault then. You should have implemented that in the destructor. That's what it's meant for. Of course you don't have to rely on the destructor and implement your own deleteLater() which will post a custom event to the event queue and trigger your own destruct process in its handler. But this is just a theory as all QObject subclasses have to be prepared to be destroyed automatically, so it should all be handled in the destructor.
Re: Q_FOREACH deleting objects
Hello guys...
Still unable to solve the problem...
I have provided the code below with the same senario what i was trying to explain in my other posts.
Without changing the class hierarchy i need to solve the problem of deleting the object.
I tried using deleteLater() .. still the problem remains... So, I cannot use deleteLater too..
Code:
#include <QtGui/QApplication>
#include <QDebug>
class parentObject
: public QObject{
public:
~parentObject(){}
protected:
void deleteSelected();
};
class child1 : public parentObject
{
public:
child1
( QObject* parent
= 0 ) : parentObject
(parent
){} ~child1(){}
void deleteSelect()
{
delete this;
}
};
class child2 : public parentObject
{
public:
child2
( QObject* parent
= 0 ) : parentObject
(parent
) {}
~child2(){
}
void deleteSelect()
{
delete this;
}
};
class child3 : public parentObject
{
public:
child3
( QObject* parent
= 0 ) : parentObject
(parent
){} ~child3(){}
void removeChild2( child2* c )
{
c->deleteSelect();
delete this;
}
};
int main(int argc, char *argv[])
{
parentObject* obj = new parentObject;
child1* c11 = new child1(obj);
c11->setObjectName ( "child1" );
child1* c12 = new child1(obj);
c12->setObjectName ( "child1" );
child1* c13 = new child1(obj);
c13->setObjectName ( "child1" );
child2* c21 = new child2(obj);
c21->setObjectName ( "child2" );
child2* c22 = new child2(obj);
c22->setObjectName ( "child22" );
child2* c23 = new child2(obj);
c23->setObjectName ( "child2" );
child3* c31 = new child3(obj);
c31->setObjectName ( "child3" );
QList<QObject*> list = obj->findChildren<QObject*>();
for( QList<QObject*>::iterator it = list.begin(); it != list.end(); ++it )
{
qDebug() << (*it)->objectName();
if( (*it
)->objectName
() == QString("child22") ) {
c31->removeChild2( c22 );
}
}
return 0;
}