13.5 Smarter Pointers
Although C++ does not support garbage collection, automatic memory management of C++ objects can be achieved in a number of ways, primarily through the use of smart pointers and reference counting. Qt offers many different smart pointer types, suited for different purposes.
A class is called a smart pointer if it overrides operator*() and operator->(), the pointer dereference operators. This enables instances to act as if they were built-in pointers. Such classes are almost always template classes, so in definitions, we must supply the referenced type in template arguments. The most common places to find these operators overloaded are in iterators and smart pointers. What makes them smart is usually due to customized behavior during construction, destruction, or assignment.
QScopedPointer is a smart pointer that automatically deletes the referenced object when the pointer goes out of scope. It is similar to std::auto_ptr. It makes no sense to copy QScopedPointers because then the referenced object would be deleted twice. The scope of the pointer clearly shows the lifetime and ownership of the referenced object.
QSharedPointer, like QScopedPointer, is a smart pointer that deletes its referenced object, but copies are permitted, and the QSharedPointer keeps a reference count. The shared heap object is deleted only when the last shared pointer to it is destroyed.
One place we have used QSharedPointer is in DataObjectTableModel, shown in Example 13.24.
Example 13.24. src/libs/dataobjects/dataobjecttablemodel.h
[ . . . . ] class DOBJS_EXPORT DataObjectTableModel : public QAbstractTableModel { Q_OBJECT public: explicit DataObjectTableModel(DataObject* headerModel = 0, QObject* parent=0); virtual bool insertRecord(QSharedPointer<DataObject> newRecord, int position = -1, const QModelIndex& = QModelIndex()); QStringList toStringList() const; QString toString() const; virtual int fieldIndex(const QString& fieldName) const; virtual ~DataObjectTableModel(); [ . . . . ] public slots: void clear(); void rowChanged(const QString& fileName); protected: QList<QSharedPointer<DataObject> > m_Data; QList<bool> m_isEditable; QStringList m_Headers; DataObject* m_Original; void extractHeaders(DataObject* hmodel); public: DataObjectTableModel& operator<<(QSharedPointer<DataObject> newObj) { insertRecord(newObj); return *this; } };
You can invoke property() and setProperty() on the DataObject indirectly with the smart pointer, just like a regular pointer, using operator->, as shown in Example 13.25.
Example 13.25. src/libs/dataobjects/dataobjecttablemodel.cpp
[ . . . . ] QVariant DataObjectTableModel:: data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); int row(index.row()), col(index.column()); if (row >= rowCount()) return QVariant(); QSharedPointer<DataObject> lineItem(m_Data.at(row)); if (lineItem.isNull()) { qDebug() << "lineitem=0:" << index; return QVariant(); } if (role == Qt::UserRole || role == Qt::ToolTipRole) return lineItem->objectName(); else if (role == DisplayRole || role == EditRole) { return lineItem->property(m_Headers.at(col)); } else return QVariant(); } bool DataObjectTableModel:: setData(const QModelIndex& index, const QVariant& value, int role) { if (index.isValid() && role == EditRole) { int row(index.row()), col(index.column()); QSharedPointer<DataObject> lineItem(m_Data.at(row)); lineItem->setProperty(m_Headers.at(col), value); emit dataChanged(index, index); return true; } return false; }
If each row is represented by a DataObject, which could exist in more than one DataObjectTableModel, by using reference-counted pointers, the table can clean up the DataObjects when it has the last shared pointer to the object.