c++qtqsharedpointer

Managing QAbstractItemModel data in QSharedPointers


In c++ Qt, I like managing my heap memory with QSharedPointers, but can/should one also use them for managing data in a QAbstractItemModel?

For example, one can have a list of QStrings QList<QSharedPointer<QString> > queue.

The problem is, that when implementing a QSharedAbstractItemModel, like the QAbstractListModel, you need to deal with raw pointers. For instance, the method index returns a QModelIndex that takes a void pointer in the constructor, pointing to one of those QStrings on the heap. As soon as you create that object, you're working with the heap memory both managed and unmanaged.

So if I store my selected item somewhere in a pointer and then clear my model, the data to that pointer gets deleted.

So how does one deal with heap memory objects that you want to put in a QAbstractItemModel

I use qt 5.1.


Solution

  • In terms of memory management, this is up to your choosing.

    QAbstractListModel needs you to write the data function which returns a QVariant. Where you create that Variant from doesn't really matter.

    The QVariant itself is a new structure , it doesn't impact your data whatsoever.

    Take this example :

     QVariant MyListImplementation::data(const QModelIndex& index, int role) const 
     {
         // QSharedPointer<QList<QString>> sharedMessageQueue
         // QList<QString>* pMessageQueue
         if (useSharedPointers)
         {
             return QVariant::fromValue(sharedMessageQueue->at(index.row()));
         } else {
             return QVariant::fromValue(pMessageQueue->at(index.row));
         }
     }
    

    So you can see two things :

    1. You create a QVariant from a value, and you are passing this QVariant by value, meaning the QVariant has his own memory, and memory management is passed to the object that requests the data (You cannot return a shared pointer of a QVariant, since you need to implement this specific method signature)

    2. The implementation is independent from the memory policy you are using for your "MessageQueue" list

    If you use shared pointer for the data in your list you won't need to worry about deallocation of the list on destructor , if you don't you need to delete the list in the class'es destructor.

    If you want a discussion about wheather is good to use QSharedPointers inside a QAbstarctListModel implementation, you will get the same answers as from the question "Is it useful and what are the trade offs when using shared pointers inside a project ?" .

    EDIT :

    Regarding your comment :

    You are worried what happens when createIndex is used to generate a QModelIndex and the use of the QModelIndex internal raw pointer.

    In QT documentation :

    Note: Model indexes should be used immediately and then discarded. You should not rely on indexes to remain valid after calling model functions that change the structure of the model or delete items. If you need to keep a model index over time use a QPersistentModelIndex.

    ...

    QModelIndex QAbstractItemModel::createIndex ( int row, int column, void * ptr = 0 ) const [protected]

    Creates a model index for the given row and column with the internal pointer ptr.

    When using a QSortFilterProxyModel, its indexes have their own internal pointer. It is not advisable to access this internal pointer outside of the model. Use the data() function instead.

    My understanding is that the QModelIndex is just a temporary structure used to retrieve data from the model. Think about it as an equivalent of a temporary table when using a database : you know you will access certain information from a table(not all the info) for multiple operations in a certain portion of your program, but you don't want to query the database for each one , you just get them as a bulk in one query ,use them according to your needs then you discard.

    QT documentation mentions a practical example of where QModelIndex can be used to access data outside the model (so instead of using model.data, you use the QModelIndex.data), but that's an exception because while the data exists in memory in the same order was entered, their indexes changed (bacause of sorting /filtering).

    In terms of memory management, the QModelIndex keeps weak pointers inside it , so indeed, if you delete the data your QModelIndex will point to a invalid location. But int terms of discarding memory you don't have to worry, since it doesn't allocate anything dinamically (weak pointers :) ).

    Where you need to worry is indeed in multithreaded environments, where you get a QModelIndex in thread A , but before thread A discards this QModelIndex your thread B deletes the Model. However, if you have this, you have a synchronization problem.

    You could bypass this whole synchronization by creating a deep copy of the QModelIndex (meaning you will copy it's internal pointer, then make it a shared pointer), but I would go with synchronization if possible.