Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Pass parent to non-pointer `QObject`-derived members?
Forum Updated to NodeBB v4.3 + New Features

Pass parent to non-pointer `QObject`-derived members?

Scheduled Pinned Locked Moved Solved General and Desktop
6 Posts 4 Posters 874 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    Bob64
    wrote on last edited by
    #1

    I have been looking at an old code base that uses Qt and noticed something that seems a bit odd.

    Probably easier to illustrate in code than explain it:

    class MyApp: public QObject {
        Q_OBJECT
        ...
    private: // data members
      A m_a;  // <<< NOTE not dynamically allocated
      ...
    };
    
    ...
    // implementation
    MyApp::MyApp() : QObject(), 
      m_a(this), m_b(this)  // <<< NOTE parent passed in
    { ...
    

    Here, A, B etc. are themselves QObject-derived classes. e.g.:

    class A : public QObject {
      Q_OBJECT
    public:
      A(QObject* parent=nullptr);
    ...
    

    The thing that surprises me here is that the m_a and m_b members will be deleted anyway when MyApp is destroyed, but by passing this into their constructors, the parent-child relationship is set up and that could lead to a double deletion.

    I am aware that there are situations where one can create QObject-based types on the stack and pass in a parent, but that one has to be very careful about lifetimes to avoid the sort of problem I just mentioned.

    Am I correct to be concerned here or am I missing something? This is an old code base that seems to be working as far as I know but the original author is no longer around to ask about it.

    J.HilkJ Christian EhrlicherC 2 Replies Last reply
    0
    • B Bob64

      I have been looking at an old code base that uses Qt and noticed something that seems a bit odd.

      Probably easier to illustrate in code than explain it:

      class MyApp: public QObject {
          Q_OBJECT
          ...
      private: // data members
        A m_a;  // <<< NOTE not dynamically allocated
        ...
      };
      
      ...
      // implementation
      MyApp::MyApp() : QObject(), 
        m_a(this), m_b(this)  // <<< NOTE parent passed in
      { ...
      

      Here, A, B etc. are themselves QObject-derived classes. e.g.:

      class A : public QObject {
        Q_OBJECT
      public:
        A(QObject* parent=nullptr);
      ...
      

      The thing that surprises me here is that the m_a and m_b members will be deleted anyway when MyApp is destroyed, but by passing this into their constructors, the parent-child relationship is set up and that could lead to a double deletion.

      I am aware that there are situations where one can create QObject-based types on the stack and pass in a parent, but that one has to be very careful about lifetimes to avoid the sort of problem I just mentioned.

      Am I correct to be concerned here or am I missing something? This is an old code base that seems to be working as far as I know but the original author is no longer around to ask about it.

      Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #3

      @Bob64 said in Pass parent to non-pointer &#x60;QObject&#x60;-derived members?:

      the parent-child relationship is set up and that could lead to a double deletion.

      There can be no doulbe deletion in your case

      in the dtor first the members of a class get destroyed before the dtor of the base class is called. Therefore m_a and m_b are already destroyed (and therefore removed from the parent's children list) before the parent cleans up it's children.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      1 Reply Last reply
      2
      • B Bob64

        I have been looking at an old code base that uses Qt and noticed something that seems a bit odd.

        Probably easier to illustrate in code than explain it:

        class MyApp: public QObject {
            Q_OBJECT
            ...
        private: // data members
          A m_a;  // <<< NOTE not dynamically allocated
          ...
        };
        
        ...
        // implementation
        MyApp::MyApp() : QObject(), 
          m_a(this), m_b(this)  // <<< NOTE parent passed in
        { ...
        

        Here, A, B etc. are themselves QObject-derived classes. e.g.:

        class A : public QObject {
          Q_OBJECT
        public:
          A(QObject* parent=nullptr);
        ...
        

        The thing that surprises me here is that the m_a and m_b members will be deleted anyway when MyApp is destroyed, but by passing this into their constructors, the parent-child relationship is set up and that could lead to a double deletion.

        I am aware that there are situations where one can create QObject-based types on the stack and pass in a parent, but that one has to be very careful about lifetimes to avoid the sort of problem I just mentioned.

        Am I correct to be concerned here or am I missing something? This is an old code base that seems to be working as far as I know but the original author is no longer around to ask about it.

        J.HilkJ Offline
        J.HilkJ Offline
        J.Hilk
        Moderators
        wrote on last edited by J.Hilk
        #2

        @Bob64 that should be fine and in certain contexts it's actually a good idea to give parents to objects allocated on the stack.

        see this SO answer for a really good explanation
        https://stackoverflow.com/a/37375210/15422846


        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


        Q: What's that?
        A: It's blue light.
        Q: What does it do?
        A: It turns blue.

        JonBJ 1 Reply Last reply
        3
        • B Bob64

          I have been looking at an old code base that uses Qt and noticed something that seems a bit odd.

          Probably easier to illustrate in code than explain it:

          class MyApp: public QObject {
              Q_OBJECT
              ...
          private: // data members
            A m_a;  // <<< NOTE not dynamically allocated
            ...
          };
          
          ...
          // implementation
          MyApp::MyApp() : QObject(), 
            m_a(this), m_b(this)  // <<< NOTE parent passed in
          { ...
          

          Here, A, B etc. are themselves QObject-derived classes. e.g.:

          class A : public QObject {
            Q_OBJECT
          public:
            A(QObject* parent=nullptr);
          ...
          

          The thing that surprises me here is that the m_a and m_b members will be deleted anyway when MyApp is destroyed, but by passing this into their constructors, the parent-child relationship is set up and that could lead to a double deletion.

          I am aware that there are situations where one can create QObject-based types on the stack and pass in a parent, but that one has to be very careful about lifetimes to avoid the sort of problem I just mentioned.

          Am I correct to be concerned here or am I missing something? This is an old code base that seems to be working as far as I know but the original author is no longer around to ask about it.

          Christian EhrlicherC Offline
          Christian EhrlicherC Offline
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on last edited by
          #3

          @Bob64 said in Pass parent to non-pointer &#x60;QObject&#x60;-derived members?:

          the parent-child relationship is set up and that could lead to a double deletion.

          There can be no doulbe deletion in your case

          in the dtor first the members of a class get destroyed before the dtor of the base class is called. Therefore m_a and m_b are already destroyed (and therefore removed from the parent's children list) before the parent cleans up it's children.

          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
          Visit the Qt Academy at https://academy.qt.io/catalog

          1 Reply Last reply
          2
          • J.HilkJ J.Hilk

            @Bob64 that should be fine and in certain contexts it's actually a good idea to give parents to objects allocated on the stack.

            see this SO answer for a really good explanation
            https://stackoverflow.com/a/37375210/15422846

            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by JonB
            #4

            @J-Hilk , @Bob64
            So let's go through this actual example, in the light of the referenced explanation/solution.

            Remember that there the poster says:

            QObject *parent = new QObject;
            QObject child(parent);
            delete parent;
            

            is the bad case, claiming that delete parent will attempt to delete child, with obvious consequences.

            MyApp::MyApp() : QObject(), 
              m_a(this), m_b(this)  // <<< NOTE parent passed in
            

            The outside world initially goes app = new MyApp. Then later, say it goes delete app. When exactly do member variables get destroyed in this scenario? Because if it does not "detach" m_a, m_b from this before the destructor of this gets to see m_a, m_b have this as their parent, we will be in the original bad situation.

            I guess the m_a, m_b varibales get detached/destroyed before the class they are in gets to run its destructor code, which would delete them? Which makes sense, but makes you think....

            If this were not so I imagine the existing code @Bob64 says is working would have fallen foul of it.

            EDIT
            While I was typing this @Christian-Ehrlicher has posted

            in the dtor first the members of a class get destroyed before the dtor of the base class is called. Therefore m_a and m_b are already destroyed (and therefore removed from the parent's children list) before the parent cleans up it's children.

            which would make sense, as suspected, and not cause a problem.

            So a C++ destructor does not get called till after its member variables have been removed? But then the destructor could not reference its member variables. I'm confused in this area.....

            before the dtor of the base class is called

            Ah! That makes sense. Calling delete on children only happens in the base destructor, and by then m_a, m_b have been removed from the derived class....

            Christian EhrlicherC 1 Reply Last reply
            1
            • JonBJ JonB

              @J-Hilk , @Bob64
              So let's go through this actual example, in the light of the referenced explanation/solution.

              Remember that there the poster says:

              QObject *parent = new QObject;
              QObject child(parent);
              delete parent;
              

              is the bad case, claiming that delete parent will attempt to delete child, with obvious consequences.

              MyApp::MyApp() : QObject(), 
                m_a(this), m_b(this)  // <<< NOTE parent passed in
              

              The outside world initially goes app = new MyApp. Then later, say it goes delete app. When exactly do member variables get destroyed in this scenario? Because if it does not "detach" m_a, m_b from this before the destructor of this gets to see m_a, m_b have this as their parent, we will be in the original bad situation.

              I guess the m_a, m_b varibales get detached/destroyed before the class they are in gets to run its destructor code, which would delete them? Which makes sense, but makes you think....

              If this were not so I imagine the existing code @Bob64 says is working would have fallen foul of it.

              EDIT
              While I was typing this @Christian-Ehrlicher has posted

              in the dtor first the members of a class get destroyed before the dtor of the base class is called. Therefore m_a and m_b are already destroyed (and therefore removed from the parent's children list) before the parent cleans up it's children.

              which would make sense, as suspected, and not cause a problem.

              So a C++ destructor does not get called till after its member variables have been removed? But then the destructor could not reference its member variables. I'm confused in this area.....

              before the dtor of the base class is called

              Ah! That makes sense. Calling delete on children only happens in the base destructor, and by then m_a, m_b have been removed from the derived class....

              Christian EhrlicherC Offline
              Christian EhrlicherC Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by Christian Ehrlicher
              #5

              @JonB said in Pass parent to non-pointer &#x60;QObject&#x60;-derived members?:

              So a C++ destructor does not get called till after its member variables have been removed? But then the destructor could not reference its member variables. I'm confused in this area.....

              You misunderstood me. The dtor of MyApp gets called, cleans up it's stuff (incl. m_a and m_b) and then the base dtor is called (= QObject's dtor) which cleans up it's children (qDeleteAll(m_children); - not that easy but enough for here). But since m_a and m_b were already cleaned up (and therefore removed from the QObject's children list) all is fine.

              hat makes sense. Calling delete on children only happens in the base destructor, and by then m_a, m_b have been removed from the derived class....

              Yey

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              1 Reply Last reply
              1
              • B Offline
                B Offline
                Bob64
                wrote on last edited by
                #6

                @Christian-Ehrlicher 's explanation in particular helped me nail what was missing from my understanding but thanks all for your replies. It was all useful discussion.

                1 Reply Last reply
                2

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved