[solved] class destructor auto-deletes pointers ?



  • I have a class which uses some widgets internally via widget pointers. I have them under private:
    @class MyClass : public QWidget{
    public:
    MyClass(); // custom constructor
    ~MyClass(); //custom destructor
    private:
    QMargins margins;
    QList<Filter *> * pFilters;

    int SAHBar_Height;
    QWidget * pContainer;
    QHBoxLayout * pLayout;
    QScrollArea * pScrollArea;
    void resizeEvent(QResizeEvent * );
    };@

    Filter is a custom class derived from QWidget. The Filter class destructor has some qDebug messages indicating when the class is going to be destructed. The pFilters contents are pointers to Filter objects, which have a pContainer parent (they are displayed inside the QHBoxLayout, contained in the pScrollArea (QScrollArea) object).
    pScrollArea gets the MyClass parent on creation, so the MyClass widget contains a QScrollArea with a pContainer viewpoert QWidget containing a QHBoxLayout layout with Filter objects (QWidgets after all) childs.

    Now, I don't understand how a class destructor works anymore because I thought I always had to manually delete any pointers and heap memory. So, if I was using a default destructor it should only delete stack allocated objects like the margins object, while any heap allocated object (the pointers) should be left untouched. If I was using a custom empty destructor it should behave the same way as a default destructor. If I was using a non-empty custom destructor, it should destroy objects as described in the destructor body.
    What happens instead is that, whatever destructor I am using, the Filter class destructor is called anyway, and the other pointers are probably deleted too, even if I don't explictly call delete on them. The strangest thing is with a custom non-empty destructor made like this:
    @MyClass::~MyClass(){
    /** deleting the QList<Filter *> * pFilters object /
    /
    next lines are commented on purpose **/
    // pFilters->clear(); // clearing the contents first. The clearing method also calls the object destructor of the content
    // delete pFilters; // deleting the remaining empty QList object

    // delete pScrollArea; // also deletes childs: pContainer, pLayout and objects from pFilters since they are childs of the scroll area

    // setting pointers to nullptr so any destructor double calls are safe
    pFilters = nullptr;
    pLayout = nullptr;
    pContainer = nullptr;
    pScrollArea = nullptr;
    }@
    As you can see I commented all delete lines. This should leave the pFilters heap object and pointer untouched, as well as the pScrollArea object. Instead, the Filter class destructor is still called and the the qDebug messages are displayed! It happens with whichever destructor type I use (default, custom empty, custom non-empty).

    Unfortunately, I can't share my source code with you. But if you have enough info, can you help me understand why the Filter objects' destructor is still called? Shouldn't it be left untouched? Or did I miss anything about class destructors?


  • Lifetime Qt Champion

    Hi,

    Indeed, you missed something but not about class destruction: Qt's parent/child relationship.

    Once a parent is getting destroyed so are his children. e.g. since you use a layout, every widget you put in becomes a child of the layout which is a child of the main widget.



  • bq. // pFilters->clear(); // clearing the contents first. The clearing method also calls the object destructor of the content
    // delete pFilters; // deleting the remaining empty QList object

    If you are expecting this to destroy the Filter objects pointed to by elements in the list then you are mistaken as a very simple experiment will confirm.
    @
    #include <QtCore>

    class Filter {
    public:
    Filter() { qDebug() << Q_FUNC_INFO << this; }
    ~Filter() { qDebug() << Q_FUNC_INFO << this; }
    };

    int main(int argc, char argv) {
    QCoreApplication app(argc, argv);
    QList<Filter
    > list;
    list = new QList<Filter
    >;
    for (int i = 0; i < 3; ++i)
    list->append(new Filter);
    qDebug() << "Before clear" << list->size();
    list->clear();
    qDebug() << "After clear (no Filter destructors called)" << list->size();
    delete list;
    qDebug() << "After delete (still no Filter destructors called)";
    // At this point the Filter objects are lost to the program and are memory leaks
    return 0;
    }
    @
    As presented there is no reason in your code for the QList<Filter
    > to be heap allocated at all.

    If you added the QWidget and QScrollArea to the QHBoxLayout and set that layout on a widget then it will be deleted by the QWidget parent child relationships. You don't say or show but if the Filter objects are QObject derived and parented then that is the reason for their destruction when their parent is destroyed.



  • [quote author="ChrisW67" date="1387315207"]If you are expecting this to destroy the Filter objects pointed to by elements in the list then you are mistaken as a very simple experiment will confirm. [/quote]
    You're right, my mistake. I, at first, assumed it was like that because I couldn't get why the destructors are still called. The docs though state clearly that the items are only removed from the list, no object destructor is called.

    As I stated before, Filter is derived from QWidget, so yes, it is a QObject. Using the objects from the class I wrote before I get this parent-child hierarchy:
    MyClass->pScrollArea->pContainer->pLayout->Filter (childs)

    At this point I think SGaist is right, I may have missed the parent-child relationship and behavior.
    [quote author="SGaist" date="1387314630"]Once a parent is getting destroyed so are his children. e.g. since you use a layout, every widget you put in becomes a child of the layout which is a child of the main widget.[/quote]
    As MyClass (QWidget) gets closed the destructor is called; but childs must be destroyed as well so their destructor is called too, and so on for each child-of-child.
    So does that mean a parent would always call the child objects' destructors because of the parent-child relationship? The MyClass destructor I wrote is called only after the childs are destructed ?



  • A QObject will destroy any child QObjects as it is destroyed. Those child objects will, as a result, destroy any child object of their own and so on.

    The QObject children will be destroyed when the QObject destructor is reached, which happens after the destructor of any derived class like MyClass has run.



  • Thank you very much!

    My app had a bug where it would run fine for 10 widgets, but would crash on exit for any number higher than 10. I fixed the destructor and now it seems to work properly, but I'll double-check all my other code as well ;)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.