[Solved] QWidget assumes allocation on heap, not stack?
-
I bought the Blanchette and Summerfield book, so my questions should get fewer. But I still am unclear about this parent object business.
Does the design assume that the objects are allocated on the heap? I guess I could just not assign a parent if the object is allocated on the stack. But the design draws conclusions from the lack of a parent. Like that the widget is a top-level window.
I have a situation where I have one QObject aggregated into another -- a QThread derivative is a member of a QCoreApplication derivative. I assume in that case I would leave the QThread's parent NULL? Even though in fact the QCoreApplication is its parent.
When I look at this -- http://doc.troll.no/4.5/objecttrees.html -- I worry because in the case that the parent is defined second, it seems to me that the parent would not just run the child's destructor, but would actually call delete on the child. The parent can't know how the child was allocated, right?
Any light appreciated.
-
what do you mean by this:
bq. I assume in that case I would leave the QThread’s parent NULL? Even though in fact the QCoreApplication is its parent.
An Object's parent can only be set through
@QObject( QObject * parent = 0 )@
@void QObject::setParent ( QObject * parent )@If you leave the Thread's parent NULL, then it does not have an parent.
-
[quote author="RickH" date="1313373180"]When I look at this -- http://doc.troll.no/4.5/objecttrees.html -- I worry because in the case that the parent is defined second, it seems to me that the parent would not just run the child's destructor, but would actually call delete on the child. The parent can't know how the child was allocated, right? [/quote]
Regardless how you create an object, if it gets a parent, the parent will try to remove it from memory by calling delete, which will implicitly call the destructor. This is by design and will always happen to child objects, if the parent is deleted.
If you create an object on the heap (or as plain member of a class) you MUST NOT set it a parent, as the memory would then be freed 2 times (by stack and parent).
-
Object allocated on stack can be given a parent as long as you can guarantee that it will be destroyed before it's parent.
And we can see many code like this:
@
void MainWindow::xxxxx(...)
{
QDialog dlg(this);
dlg.exec();
...
}
@ -
Ok, that's correct.
For this case it works, but you MUST have the guarantee, otherwise you get unexpected results. -
In the case that Hobby mentioned, does MainWindow call delete on dlg? What happens if you call delete on an object allocated on the stack?
Ohhhhhh. It just hit me. Is it that when dlg is deleted, its destructor tells the MainWindow it is ceasing to exist, and the MainWindow removes it from its list of children? Then when the MainWindow is deleted, it doesn't call delete on the child. Right?
-
[quote]
Ohhhhhh. It just hit me. Is it that when dlg is deleted, its destructor tells the MainWindow it is ceasing to exist, and the MainWindow removes it from its list of children? Then when the MainWindow is deleted, it doesn’t call delete on the child. Right?
[/quote]
Correct :)However NEVER do this:
@
MainWindows *w = new MainWindow;
Dialog d(w);
delete w;
@ -
[quote author="RickH" date="1313403794"]In the case that Hobby mentioned, does MainWindow call delete on dlg? What happens if you call delete on an object allocated on the stack? [/quote]
Then you get exceptions. AFAIK, officially, the behavior is undefined :-)
If a QObject is destroyed, it tells it's parent about the fact and then the parent removes it from the list of children. -
[quote author="RickH" date="1313403794"]What happens if you call delete on an object allocated on the stack?[/quote]
In general:
@
void myFunction()
{
MyObject obj;
// do something
delete &obj; // this works
// do something more
} // it crashes here
@The call in line 5 is ok execution wise. It calls the destructor of the MyObject class held in variable pointer. The catastrophe starts at the end of the function (end of the variable scope of obj, actually). Because obj is created on the stack, the compiler calls the destructor again. As the object is already destroyed, this will fail and you are very, very likely to having your program crash.
Specific to QObjects:
@
void myFucntion()
{
QObject *parentObject = new QObject;
QObject childObject(&parentObject);
// do your work here
delete parentObject; // you need this to prevent a memory leak!
} // bang! on childObject
@The above code seems ok, but it will crash on the exit of the function too. You must delete the parentObject, because it's created on the heap and you have a memory leak otherwise. But due to the parent-child relationship, the destructor of parentObject calls the destructor of childObject, effectively resulting in a "delete &childObject". From this point on the same catastrophe is on its way as in the first example, because the compiler generated a call on childObject's constructor at the end of the function.
You can mix stack/heap allocation and set the parent of a stack-allocated object. But you must make absolutely sure that the stack-allocated object is destroyed before the parent object! This is insofar non-critical, because if a OQbject tells its parent that it no longer is living and the parent removes it from its list of children then.
As a rule of thumb: If in doubt use heap allocation with new and explicitly call delete.
-
Interesting discussion.
So in my case of one QObject derivative being a member of another QObject derivative:
@QCoreAppDeriv : public QCoreApplication {
QThreadDeriv myThread;
:
}@The way C++ is set up, the destructor for the object-as-a-whole is called before the destructors for the objects-who-are-members. So I have to not set the parent of the member to be the object-as-a-whole. If I do, the object-as-a-whole will call delete on the member.
-
[quote author="RickH" date="1313418131"]
The way C++ is set up, the destructor for the object-as-a-whole is called before the destructors for the objects-who-are-members. So I have to not set the parent of the member to be the object-as-a-whole. If I do, the object-as-a-whole will call delete on the member.
[/quote]Yes, that's correct.
And the destructor fthe object-as-a-whole will be ran before the destructors of the members.If you need parent-child relationship in this use case, use a pointer and new instead of an object variable.