Smart pointers are a very important mechanism for controlling the lifetime of objects. In Qt, there is a model for controlling the lifetime of objects, when objects are inherited from the QObject base class and a “relationship” is set - parent / child. When deleting an object, it deletes all of its child. This model of managing the lifetime of objects fits very well with the technology of interaction between the objects of “Signals & Slots”, and using smart pointers can cause hard-to-debug bugs.
This article will discuss the QScopedPointer smart pointer, which is a “lightweight” version of QSharedPointer. QScopedPointer is introduced in version 4.6.
In boost, there are analogues of Qt-shny smart points:
QSharedPointer - boost :: shared_ptr
QWeakPointer - boost :: weak_ptr
QScopedPointer - boost :: scoped_ptr
Example 1
A simple program that does not remove the m_socket object and, accordingly, when the application terminates, the socket is not disabled:
')
class test_socket : public QTcpSocket { public: test_socket( QObject * parent ) : QTcpSocket( parent ) { } virtual ~test_socket( ) { qDebug( ) << "~test_socket"; } }; class test_scop_ptr_obj : public QObject { Q_OBJECT test_socket * m_socket; public: test_scop_ptr_obj( ) : m_socket( new test_socket( 0 ) ) { m_socket->connectToHost( "www.ru", 80 ); connect( m_socket, SIGNAL( stateChanged ( QAbstractSocket::SocketState ) ), SLOT( on_state_changed( QAbstractSocket::SocketState ) ) ); } virtual ~test_scop_ptr_obj( ) { qDebug( ) << "~test_scop_ptr_obj"; } private Q_SLOTS: void on_state_changed( QAbstractSocket::SocketState socket_state ) { qDebug( ) << socket_state; } }; int main( int argc, char** argv ) { QCoreApplication a( argc, argv ); test_scop_ptr_obj obj; QTimer::singleShot( 3000, &a, SLOT( quit( ) ) ); return a.exec( ); }
After starting the program we get the following output:
QAbstractSocket :: ConnectingState
QAbstractSocket :: ConnectedState
~ test_scop_ptr_obj
Example 2
Now change the line:
: m_socket( new test_socket( 0 ) )
per line:
: m_socket( new test_socket( this ) )
and get the output:
QAbstractSocket :: ConnectingState
QAbstractSocket :: ConnectedState
~ test_scop_ptr_obj
~ test_socket
Example 3
Add a smart point and now the test_scop_ptr_obj class will look like this:
class test_scop_ptr_obj : public QObject { Q_OBJECT QScopedPointer< test_socket > m_socket; public: test_scop_ptr_obj( ) : m_socket( new test_socket( 0 ) ) { m_socket->connectToHost( "www.ru", 80 ); connect( m_socket.data( ), SIGNAL( stateChanged ( QAbstractSocket::SocketState ) ), SLOT( on_state_changed( QAbstractSocket::SocketState ) ) ); } virtual ~test_scop_ptr_obj( ) { qDebug( ) << "~test_scop_ptr_obj"; } private Q_SLOTS: void on_state_changed( QAbstractSocket::SocketState socket_state ) { qDebug( ) << socket_state; } };
We get the output:
QAbstractSocket :: ConnectingState
QAbstractSocket :: ConnectedState
~ test_scop_ptr_obj
~ test_socket
QAbstractSocket :: UnconnectedState
Analysis
Example 1 is not interesting, since in this example there are memory leaks.
Example 2 and 3 differ in the technology of releasing previously allocated memory. In either example, the test_socket class object is deleted, but in the 3rd example, the on_state_changed function is called after the ~ test_scop_ptr_obj destructor call, which is an error. Before removing its child, the parent object makes disconnects to its signals and slots, so in example 2 the stateChanged signal to the test_scop_ptr_obj object does not come.
Results
To correct the error in example 3, it is necessary to add in the test_scop_ptr_obj destructor disable the signals of the object being deleted:
virtual ~test_scop_ptr_obj( ) { m_socket->disconnect( ); qDebug( ) << "~test_scop_ptr_obj"; }
In this case, the output will be the same as in example 2.