6

So, I think I've searched the web quite thoroughly about this and found nothing really useful (just confusing at most...).

I'd like to know how I can (if possible) use Qt with non-dynamic memory. The problem I face is that for many widgets, I know exactly what I want to use (these sub-widgets, these layouts, in fixed numbers, etc.). Yet, everything in Qt seems to get in the way when you don't use dynamic memory. A simple example is QLayout, which from the Qt documentation is designed to take ownership of anything it is added. So basically, the following code:

//In header
class ThumbnailDialog : public QDialog
{
    Q_OBJECT
public:
    ThumbnailDialog(QWidget* parent = 0);
    ~ThumbnailDialog(void);
private:
    QPushButton m_confirm;
    QPushButton m_cancel;

    QHBoxLayout m_buttonsLayout;
};

//Implementation of ctor
ThumbnailDialog::ThumbnailDialog(QWidget* parent):
    QDialog(parent)
{
     //...
     m_buttonsLayout.addWidget(&m_confirm);
     m_buttonsLayout.addWidget(&m_cancel);
     //...
     setLayout(&m_dialogLayout);
}

...will end up (on MSVC) in a debug assertion fail for _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) because, in the ThumbnailDialog's dtor, the layout tries to delete the buttons... which it obviously shouldn't.

So, am I forced to use dynamic memories everywhere, as this "Qt Expert" advocates (while mentioning "heap", though...) ? This seems wrong as this prevents taking advantage of RAII (if parent-child relation means there'll be a delete, one can't use smart pointers to do that I suppose, then). It also feels terribly wrong to resort to dynamic memory for things known at compile-time... (but I could be wrong, that's just my feeling).

So: is there any way of using Qt without resorting to dynamic memory and news for every single widget/layout ?

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
JBL
  • 12,588
  • 4
  • 53
  • 84
  • I think Qt designed in that way. But if it guarantees the deletion of child objects when parent is destroyed - it also acceptable approach. – vahancho Dec 13 '13 at 14:44
  • @JBL, I think you are overthinking this. If you are using QT (or any other UI library), say bye bye to efficiency on such small things already. They are built on top of layers over layers of passing values around while doing nothing, a couple `new`s won't change anything. It is also not conceptually wrong. If you can allocate a lot of objects on the fly, there is no need to make them static, force it to be written to the executable and stored on disk. Not to mention later you would regret not being able to have a more dynamic UI. – Shahbaz Dec 13 '13 at 14:57
  • There are no exception issues, non of the Qt API's throw. – paulm Dec 14 '13 at 00:10
  • @paulm, I believe he is talking about out of memory exception that can be thrown by `new`. – Shahbaz Dec 14 '13 at 15:31
  • @JBL, If you want to statically allocate your object, it would be put in the executable and loaded when you execute it. If you don't have enough memory for the executable, your program can't run. If you do have enough memory for it, then `new` won't throw an exception. Again, you are worrying about things that are not really an issue. In fact, C and C++ are some of the very few languages were you actually explicitly manage memory and there are 1000 other languages that do all allocations dynamically and they still run just fine. – Shahbaz Dec 14 '13 at 15:34
  • If new throws then at the point you're likely dead anyway (if throwing when creating the UI, there is no recovery). http://stackoverflow.com/questions/5548951/why-doesnt-qt-use-exception-handling – paulm Dec 14 '13 at 17:16
  • Also worth a read: http://stackoverflow.com/questions/1308052/policy-with-catching-stdbad-alloc – paulm Dec 14 '13 at 17:23

5 Answers5

4

I think you are misunderstanding the problem here. You are not double deleting something, you are deleting an object allocated on the stack:

 int foo = 12345;
 int* pFoo = &foo;
 delete pFoo;

This is what happens when you pass a pointer to a stack based object to QHBoxLayout, hence your heap corruption debug assert.

Qt manages QObjects this way because most GUI's have LOTS of widgets, which makes managing GUI object life times easier, it also allows queued deletes across threads etc. Also internally most classes use PIMPL's so you are not avoiding the heap/dynamic allocation even if your code worked.

Having said all this you can get it to work without the heap alloc if you wish, but it is far more effort than its worth. In your example you would have to remove the widgets from the layout in the destructor, but be sure the nullptr check things in case the ctor has thrown, if they're not in the layout when its destructor is hit then it won't be able to delete them.

One more thing to think about.. if Qt was designed to work this way then you could easily end up in the situation where you overflow the stack on some platforms. E.g Qt works on Symbian/S60 which has extremely limited stack, running your code there could easily cause a stackoverflow :).

paulm
  • 5,629
  • 7
  • 47
  • 70
3

Yes, there is. (and in a pretty clean way, I believe)

You just have to be aware about the order of destruction of your QObject hierarchy:

  • As explained in the Qt docs, a lot of objects claim ownership of their children and therefore want to make sure they get cleaned up when they die.
  • QObject’s dtor relieves its parent of that ownership.
  • Destruction of non-static data members happens from bottom to top.

Which means that, in your case, you just have to move the layout to the top:

private:
    QHBoxLayout m_buttonsLayout;

    QPushButton m_confirm;
    QPushButton m_cancel;

All the layout’s children’s dtors unregister themselves from the layout, so when the layout destruction happens there are no registered childs left. Previously the layout’s destruction deleted the children which where not newd in the first place.

This may seem tedious at first sight, but in practice the hierarchy within a custom widget is pretty flat. The order of widgets in one layer of the hierarchy – in this case the QPushButtons – is not important which usually means that you only have to arrange the layouts on top properly.

Darklighter
  • 2,082
  • 1
  • 16
  • 21
2

The problem you're seeing is that you're not thinking about what is happening. The QPushButton instances in the class are created and owned by the class. When the class is deleted, it will call the destructor on the QPushButtons.

Qt provides useful parenting of objects and deleting a parent will handle deletion of all its child objects.

So, in the case of your example, you have two things that are going to call the destructor on the QPushButtons: 1) The deletion of the class object for ThumbnailDialog and 2) The deletion of the buttonlayout, which will try to delete its children and will fail, as the object is on the stack.

What you can do, if you really want, is ensure that the QPushButton items are removed from the button layout, in the destructor of ThumbnailDialog by calling removeWidget() on the button layout.

However, this is messy. Dynamic allocation is a much better method, as it allocates objects on the heap and not the stack.

Also note, with Qt's parenting, it means that you can create a lot of widgets without needing to keep track of them. For example, you can do this: -

ThumbnailDialog::ThumbnailDialog(QWidget* parent):
    QDialog(parent)
{
     //...
     m_buttonsLayout.addWidget(new QPushButton("Confirm"));
     m_buttonsLayout.addWidget(new QPushButton("Cancel"));
     //...
     setLayout(&m_dialogLayout);
}

In this case, the push buttons don't even need to be defined in the header and you can be assured that they'll be deleted along with their parent.

TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • For items you need to connect to, create the items with pointers to them, add them to the layout and then use the pointers to setup the signals and slots, or just keep smart (weak) pointers in the class to those that you need access to later on. – TheDarkKnight Dec 13 '13 at 15:40
1

No, not really. I'm afraid. Qt relies on this stuff.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • If you manually remove all children (e.g. `QLayout::removeWidget`), there should be no more double `delete`s, I guess... – Johannes S. Dec 13 '13 at 14:50
1

If you have read the other answers and you still want to use static allocation, you can release the ownership that was taken when doing addWidget():

     //...
     m_buttonsLayout.addWidget(&m_confirm);
     m_buttonsLayout.addWidget(&m_cancel);

     // release the ownership by setting no parent
     m_confirm.setParent(0);
     m_cancel.setParent(0);

     //...
lionel
  • 33
  • 4