Results 1 to 6 of 6

Thread: Smart pointers and parented QObjects

  1. #1
    Join Date
    Feb 2016
    Posts
    3
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Smart pointers and parented QObjects

    We would like to combine the benefits of a smart pointer, we don't care which one(s), and parented QObjects with lifespans shorter than their parents. Specifically, if a parent QObject has a member smart pointer to a child, and that child may get destroyed and recreated several times through the parent's existence. It would be great to use a smart pointer for the child, as a developer wouldn't leak memory accidentally - at least for the duration of the life of the parent - if they failed to check the member's existence before creating a new instance.

    We run into an issue with smart pointers (specifically std::shared_ptr) during the Parent's destruction. This can lead to a segfault. We suspect that the child is destroyed by its parent because it is owned by the parent and then when the shared_ptr goes out of scope destruction tries to happen again, although the order of this is somewhat suspect to us. We would think that the shared_ptr would go out of scope first as the Parent's destructor is called, which would in turn call delete on the owned object and then the base class QObject's destructor would be called, which would try and delete the Child (who it isn't aware was deleted by the shared_ptr, for some reason). Some searching of various forums indicates that smart pointers shouldn't be mixed with parented objects for this reason.

    Here's a crash stack trace for a parented QTimer that was wrapped in a smart pointer.
    Qt Code:
    1. crashlog.20160223-141028.3295:*** SIGSEGV (@0x7f4d4c4afbb8) received by PID 3295 (TID 0x7f4d823d8a80) from PID 1279982520; stack trace: ***
    2. crashlog.20160223-141028.3295- @ 0x7f4d793b6d40 (unknown)
    3. crashlog.20160223-141028.3295- @ 0x7f4d7a9d395f QObject::killTimer()
    4. crashlog.20160223-141028.3295- @ 0x7f4d7a9d8e01 QTimer::stop()
    5. crashlog.20160223-141028.3295- @ 0x7f4d7a9d8e91 QTimer::~QTimer()
    6. crashlog.20160223-141028.3295- @ 0x7f4d80f787c9 std::shared_ptr<>::~shared_ptr()
    To copy to clipboard, switch view to plain text mode 

    Regardless of our understanding of the crash, we would prefer to know the correct usage (if any) for mixing smart pointers and parented QObjects. We realize that in many cases parenting a QObject is unnecessary if a smart pointer is used, such as the above QTimer, but there are examples when the Qt parenting scheme is quite useful such as UI elements.

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,311
    Thanks
    314
    Thanked 870 Times in 857 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Smart pointers and parented QObjects

    we would prefer to know the correct usage (if any) for mixing smart pointers and parented QObjects
    The only way you can do this is using Qt's built-in QPointer. This is designed for storing QObject pointers and has the semantics that the wrapped pointer is automatically set to null when the pointer goes out of scope. QPointers are weak, as opposed to strong boost:: or std:: shared_ptrs. I am not certain of the internal implementation, but QPointer likely uses QObject's destroyed() signal to keep track of the pointer's lifetime.

    Of course, this begs the question of why you would want to dynamically create and destroy child objects in the first place. If these are QWidget instances, the usual convention is to either create them on the stack (as is common with QDialog), or to show() / hide() them on demand.

  3. #3
    Join Date
    Oct 2009
    Location
    Germany
    Posts
    120
    Thanked 42 Times in 41 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Smart pointers and parented QObjects

    @op: Just out of curiosity: can you give us a use case for this? I'm afraid using STL or boost shared/unique pointers will cause more trouble than benefit. Using QObject parent/child relationship and one of the strong pointers on the same object instance you have two mechanisms controlling object life time that do not know of each other. This will sooner or later lead to crashes that are hard to track. As d_stranz already suggested you might better go with Qt smart pointers. However, QPointer might not be what you want. You may also consider QSharedPointer, which, by its documentation, seems to be a counterpart of std::shared_ptr which is aware of the special needs of Qt. Here's a small example showing the difference between QPointer and QSharedPointer. MyClass is a simple QObject derived class logging its creation and deletion.
    Qt Code:
    1. #include "MyClass.h"
    2.  
    3. #include <iostream>
    4.  
    5. MyClass::MyClass(QObject *parent) :
    6. QObject(parent)
    7. {
    8. std::cout << "constructing MyClass object " << this << std::endl;
    9. }
    10.  
    11. MyClass::~MyClass()
    12. {
    13. std::cout << "deleting MyClass object " << this << std::endl;
    14. }
    To copy to clipboard, switch view to plain text mode 
    The MainWindow CTOR looks like
    Qt Code:
    1. #include "MainWindow.h"
    2. #include "ui_MainWindow.h"
    3.  
    4. #include "MyClass.h"
    5.  
    6. #include <QPointer>
    7. #include <QSharedPointer>
    8.  
    9. #include <iostream>
    10.  
    11. MainWindow::MainWindow(QWidget *parent) :
    12. QMainWindow(parent),
    13. ui(new Ui::MainWindow)
    14. {
    15. ui->setupUi(this);
    16. std::cout << "MainWindow::CTOR begin" << std::endl;
    17. {
    18. std::cout << "creating QPointer" << std::endl;
    19. QPointer<MyClass> p = new MyClass(this);
    20. std::cout << "creating QSharedPointer" << std::endl;
    21. QSharedPointer<MyClass> sp(new MyClass(this));
    22. }
    23. std::cout << "MainWindow::CTOR end" << std::endl;
    24. }
    To copy to clipboard, switch view to plain text mode 
    When running the program I get
    Qt Code:
    1. MainWindow::CTOR begin
    2. creating QPointer
    3. constructing MyClass object 0x13defe0
    4. creating QSharedPointer
    5. constructing MyClass object 0x110f780
    6. deleting MyClass object 0x110f780
    7. MainWindow::CTOR end
    To copy to clipboard, switch view to plain text mode 
    The first MyClass object pointer 0x13defe0 is assigned to the QPointer instance, the second one (0x110f780) to QSharedPointer instance. Immediately after construction both pointers go out of scope. Only the MyClass object controlled by the QSharedPointer gets deleted.

    Depending on your use case that might be a better choice.

    Best regards
    ars

  4. #4
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Smart pointers and parented QObjects

    Quote Originally Posted by jason.gibbs@x-navtech.com View Post
    We would like to combine the benefits of a smart pointer, we don't care which one(s), and parented QObjects with lifespans shorter than their parents. Specifically, if a parent QObject has a member smart pointer to a child, and that child may get destroyed and recreated several times through the parent's existence. It would be great to use a smart pointer for the child, as a developer wouldn't leak memory accidentally - at least for the duration of the life of the parent - if they failed to check the member's existence before creating a new instance.
    (emphasis is mine)
    Then you shouldn't use reference-counted smart pointers such as std::shared_ptr or QSharedPointer, because they precisely allow objects to escape a scope. The parent QObject is already managing the child's lifetime; everyone else can just manipulate a plain Child * pointer without worrying when to delete it (but must still be careful not to access the child after the parent has been destroyed).

    I suggest one of those two options:
    • store the pointer to the child in a plain Child * private member, and add a helper method void resetChild(Child *newChild) that deletes the existing child, if any (or calls deleteLater(), depending on your needs), before setting the new pointer. Explain to the developers that they must go through this method to set up a new child.
    • if that is not enough (i.e. some developers keep modifying the Child * member directly), wrap the Child * in a smart pointer that forces updates to go through the method. std::unique_ptr (or QScopedPointer) do the trick, except that the parent's destructor must take care of calling unique_ptr::release() (or QScopedPointer::take()) to avoid double deletion. Or just implement a minimal ad hoc smart pointer yourself, which only initializes the Child * to NULL and forces updates to delete the existing child first.

  5. #5
    Join Date
    Feb 2016
    Posts
    3
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Smart pointers and parented QObjects

    Thank you all for your feedback. There were a few questions about why we would like to do this. Here's an example we ran across that prompted this discussion in our group. Suppose there is a dialog with the following properties:


    1. it is complex and easier to construct from scratch when it is used than manage update/reset rules
    2. it is modeless
    3. we never want multiple instances of this dialog displayed


    We want this dialog to be recreated whenever a particular slot in the parent is called and either deleted-on-closed or possibly deleted by a separate slot call.

    Essentially this lead to our thinking that it would be fantastic if we could get all the benefits of the parent/child UI relationship without mandatory ownership by the parent. Some "emancipated minor" option to ask "hey, Parent- can you teach me where i fit in the UI world but not be in charge of my life after that?" A decoupling of object ownership and UI parenting benefits.


    store the pointer to the child in a plain Child * private member, and add a helper method void resetChild(Child *newChild) that deletes the existing child, if any (or calls deleteLater(), depending on your needs), before setting the new pointer. Explain to the developers that they must go through this method to set up a new child.
    if that is not enough (i.e. some developers keep modifying the Child * member directly), wrap the Child * in a smart pointer that forces updates to go through the method. std::unique_ptr (or QScopedPointer) do the trick, except that the parent's destructor must take care of calling unique_ptr::release() (or QScopedPointer::take()) to avoid double deletion. Or just implement a minimal ad hoc smart pointer yourself, which only initializes the Child * to NULL and forces updates to delete the existing child first.
    Agreed - both are ways to accomplish this task. We were trying to find a way to use a more C++11(and beyond) approach to handling this situation - especially as we bring new developers into the fold. Our goal is to eliminate "delete" in our codebase if possible.

    We really appreciate the time and effort put in to the replies. Thank you so much!

  6. #6
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,311
    Thanks
    314
    Thanked 870 Times in 857 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Smart pointers and parented QObjects

    1. it is complex and easier to construct from scratch when it is used than manage update/reset rules
    2. it is modeless
    3. we never want multiple instances of this dialog displayed
    1. This is puzzling. Whatever you do upon construction could be factored into an initialization method called from the constructor or a reset() method.
    2. That just makes it a little awkward to use a stack-based instance to control lifetime
    3. So what happens if the dialog already is on display? Does executing the action that invokes it do nothing, thus causing the user to try to execute it again in frustration? Does the existing dialog get moved to the top with whatever contents it currently contains, thus confusing the user? Does the existing dialog vanish only to get replaced with a clean new version, thus annoying the user who was editing or observing something when the action invoking the new dialog was executed from behind the scenes somehow?


    To me, this says that you are trying to wrap smart pointer functionality around something that should instead be controlled by business logic. The business logic should determine that the action(s) which can invoke the dialog must be disabled if the dialog is already in existence. If your developers are too undisciplined to abide by the business rules, then code review and testing will uncover the violation.

    You could implement your dialog as a singleton with a private constructor and an instance() method. That could ensure that only one is in existence, but it wouldn't help with the issues I raised in (3) above. It would help with the update/reset issue, but only if your singleton was constructed in a way that deleted the instance upon closure of the dialog.

Similar Threads

  1. Replies: 2
    Last Post: 31st August 2013, 13:46
  2. Multiple inheritance of QObjects
    By tescrin in forum Qt Programming
    Replies: 1
    Last Post: 7th January 2013, 22:57
  3. Serialization Of Xml using QObjects
    By StarRocks in forum Qt Programming
    Replies: 30
    Last Post: 4th January 2013, 16:55
  4. Replies: 3
    Last Post: 6th April 2012, 16:44
  5. smart pointers in Qt and Boost
    By pospiech in forum Qt Programming
    Replies: 0
    Last Post: 18th April 2010, 14:24

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Qt is a trademark of The Qt Company.