Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QSharedPointer in destructed signal parameter deletes object which has other QSharedPointer



  • Hello.

    Could someone explain or point me to a best practices guide for proper use of QSharedPointers as parameters to constructors and signals?

    I have an example code below:

    • In Manager::start function, I emit Object::start, which calls the connected Object::onStart slot.
    • In Object::onStart slot, I emit Object::started signal with Event parameter (that has ObjectSharedPointer), which calls the connected Manager::onStarted slot.
    • After Manager::onStarted slot, the Event parameter naturally gets deleted when it is out of scope.
    • This, in turn, deletes the ObjectSharedPointer member of Event. However, this deletes the object pointed to by the shared pointer, although there is still a shared pointer in Manager::_objects vector.
    • On exit (Ctrl+C), the destructor of the shared pointer vector Manager::_objects causes a crash in this case.

    Is this related to passing const& to the shared pointer in Event constructor?
    How do we properly construct Event and emit a signal with it?

    (Please ignore cosmetic issues)

    output

    "@0x00000000011be2d0 Manager::Manager()"
    "@0x00000000011c0460 Object::Object( Object 0 )"
    "@0x00000000011bff90 Object::Object( Object 1 )"
    "@0x00000000011be2d0 Manager::start()"
    "@0x00000000011c0460 Object::onStart( Object 0 )"
    "@0x00000000011c0460 Object::onStart::emit( Event( Object 0 ) )"
    "@0x00007fffc64d4ff0 Event::Event( Object 0 )"
    "@0x00000000011be2d0 Manager::onStarted( Event@0x00007fffc64d4ff0( Object 0 ) )"
    "@0x00007fffc64d4ff0 Event::~Event( Object 0 )"
    "@0x00000000011c0460 Object::~Object( Object 0 )"
    "@0x00000000011bff90 Object::onStart( Object 1 )"
    "@0x00000000011bff90 Object::onStart::emit( Event( Object 1 ) )"
    "@0x00007fffc64d4ff0 Event::Event( Object 1 )"
    "@0x00000000011be2d0 Manager::onStarted( Event@0x00007fffc64d4ff0( Object 1 ) )"
    "@0x00007fffc64d4ff0 Event::~Event( Object 1 )"
    "@0x00000000011bff90 Object::~Object( Object 1 )"
    ^C
    "@0x00000000011be2d0 Manager::~Manager()"
    Segmentation fault (core dumped)
    

    test.hpp

    #include <QtCore/QSharedPointer>
    #include <QtCore/QVector>
    
    
    class Object;
    typedef QSharedPointer<Object> ObjectSharedPointer;
    typedef QVector<ObjectSharedPointer> ObjectVector;
    
    
    class Event
    {
    public:
        ObjectSharedPointer _object;
    
    public:
        Event( const ObjectSharedPointer &object = nullptr );
        ~Event();
    };
    Q_DECLARE_METATYPE( Event )
    
    
    class Object : public QObject
    {
        Q_OBJECT
    
    public:
        QString _name;
    
    public:
        Object( const QString &name );
        ~Object();
    
    signals:
        void start();
        void started( const Event &event );
    
    private slots:
        void onStart();
    };
    
    
    class Manager : public QObject
    {
        Q_OBJECT
    
    public:
        ObjectVector _objects;
    
    public:
        Manager();
        ~Manager();
    
        void start();
    
    private slots:
        void onStarted( const Event &event );
    };
    

    test.cpp

    #include <csignal>
    #include <cstdlib>
    #include <QtCore/QDebug>
    #include <QtCore/QCoreApplication>
    #include "test.hpp"
    
    template <typename T>
    QString getPointer( const T *object )
    {
        return QString( "0x%1" ).arg( reinterpret_cast<unsigned long long int>( object ), sizeof( T* ) * 2, 16, QChar( '0' ) );
    }
    
    QString getObjectName( const ObjectSharedPointer &object ) { return QString( object.isNull() ? "nullptr" : object->_name ); } 
    
    Event::Event( const ObjectSharedPointer &object ) : _object( object ) { qDebug() << QString( "@%1 %2( %3 )" ).arg( getPointer( this ) ).arg( "Event::Event" ).arg( getObjectName( _object ) ); }
    Event::~Event() { qDebug() << QString( "@%1 %2( %3 )" ).arg( getPointer( this ) ).arg( "Event::~Event" ).arg( getObjectName( _object ) ); }
    
    Object::Object( const QString &name ) : _name( name )
    {
        qDebug() << QString( "@%1 %2( %3 )" ).arg( getPointer( this ) ).arg( "Object::Object" ).arg( _name );
        QObject::connect( this, &Object::start, this, &Object::onStart );
    }
    Object::~Object()
    {
        qDebug() << QString( "@%1 %2( %3 )" ).arg( getPointer( this ) ).arg( "Object::~Object" ).arg( _name );
    }
    void Object::onStart()
    {
        qDebug() << QString( "@%1 %2( %3 )" ).arg( getPointer( this ) ).arg( "Object::onStart" ).arg( _name );
        qDebug() << QString( "@%1 %2( Event( %3 ) )" ).arg( getPointer( this ) ).arg( "Object::onStart::emit" ).arg( _name );
        emit started( Event( ObjectSharedPointer( this ) ) );
    }
    
    Manager::Manager()
    {
        qDebug() << QString( "@%1 %2()" ).arg( getPointer( this ) ).arg( "Manager::Manager" );
    
        qRegisterMetaType<Event>( "Event" );
        for ( int i = 0; i < 2; ++i )
        {
            Object *object = new Object( QString( "Object %1" ).arg( _objects.size() ) );
            QObject::connect( object, &Object::started, this, &Manager::onStarted );
            _objects.append( ObjectSharedPointer( object ) );
        }
    }
    Manager::~Manager() { qDebug() << QString( "@%1 %2()" ).arg( getPointer( this ) ).arg( "Manager::~Manager" ); }
    void Manager::start() { qDebug() << QString( "@%1 %2()" ).arg( getPointer( this ) ).arg( "Manager::start" ); for ( ObjectSharedPointer &object : _objects ) { emit object->start(); } }
    void Manager::onStarted( const Event &event )
    {
        qDebug() << QString( "@%1 %2( Event@%3( %4 ) )" ).arg( getPointer( this ) ).arg( "Manager::onStarted" ).arg( getPointer( &event ) ).arg( getObjectName( event._object ) );
    }
    
    QCoreApplication *application;
    Manager *manager;
    
    void onSignal( int signal )
    {
        qDebug() << "";
        delete manager;
        application->quit();
    }
    
    int main( int argc, char **argv )
    {
        ::signal( SIGINT, onSignal );
        ::signal( SIGTERM, onSignal );
    
        application = new QCoreApplication( argc, argv );
        manager = new Manager();
        manager->start();
        return application->exec();
    }
    

  • Qt Champions 2019

    @fatih.erol said in QSharedPointer in destructed signal parameter deletes object which has other QSharedPointer:

    emit started( Event( ObjectSharedPointer( this ) ) );

    You're creating a new QSharedPointer here which gets destroyed after the signal is done so the object gets destroyed.

    And why do you create a QApplication instance on the heap?


  • Qt Champions 2017

    And why would you hold a QObject in a shared pointer to begin with? You're asking for trouble ...



  • Thank you. I should have read QEnableSharedFromThis documentation.

    You can inherit this class when you need to create a QSharedPointer from any instance of a class; for instance, from within the object itself. The key point is that the technique of just returning QSharedPointer<T>(this) can not be used, because this winds up creating multiple distinct QSharedPointer objects with separate reference counts. For this reason you must never create more than one QSharedPointer from the same raw pointer.
    

    I guess, the pointer was being smart, but I was not, by initiating one from 'this' pointer in signal. That does not really convey ownership semantics particularly. I must have assumed the references were counted globally based on shared pointer objects pointing to the raw pointer value. Event member could be a raw pointer instead.

    The example was carved roughly to test the issue. No particular reason QCoreApplication was a pointer; I pulled it out of main to handle 'Ctrl+C', although the method might go against "You can't call Qt functions from Unix signal handlers." Also, I wanted to explicitly delete Manager on 'Ctrl+C'.

    QSharedPointer documentation does not have any warning about pointing to QObject-derived classes. Actually, it does so in examples setting custom deleter to Object::deleteLater. I guess, it can conflict with Qt's parent/child mechanism.


Log in to reply