Qt Implicit Sharing

2017/12/15 19:16:27


Qt Implicit Sharing

We can often find some code blocks in Qt source code as follows:

if (impl && !impl->ref.deref())
    delete impl;

Or:

impl = x.imp
if (impl)
    impl->ref.ref();

What does these code mean?

Overview

Many C++ classes in Qt use implicit data sharing to maximize resource usage and minimize copying. Implicitly shared classes are both safe and efficient when passed as arguments, because only a pointer to the data is passed around, and the data is copied only if and when a function writes to it, i.e., copy-on-write.[Qt document]
.
Briefly speaking, the implicit sharing works as smart pointer, especially as shared pointer. The implicit sharing has a reference which means the reference count by other object. As shared pointer, the count will increase or decrease whenever a object will reference or deference to it.
The implicit sharing mainly focuses on deep and shallow copies. Object assignment (with operator=()) for implicitly shared objects is implemented using shallow copies.
The benefit is we only do deep copy when we actually change the data.

void QPen::setStyle(Qt::PenStyle style)
{
    detach();           // detach from common data
    d->style = style;   // set the style member
}

void QPen::detach()
{
    if (d->ref != 1) {
        ...             // perform a deep copy
    }
}

Customized Shared

Qt provides QSharedData and QSharedDataPointer to implement customized shared classes.
QSharedData is a base class for shared data objects. QSharedData is designed to be used with QSharedDataPointer or QExplicitlySharedDataPointer to implement custom implicitly shared or explicitly shared classes. QSharedData provides thread-safe reference counting.
QSharedDataPointer represents a pointer to an implicitly shared object.
Here will give an example to customize your own implicit sharing object.

Suppose you want to make an Employee class implicitly shared. The procedure is:

  1. Define the class Employee to have a single data member of type QSharedDataPointer.(Shared pointer).
  2. Define the EmployeeData class derived from QSharedData to contain all the data members you would normally have put in the Employee class.(Actually it is the famous D pointer)
#ifndef EMPLOYEE_H
#define EMPLOYEE_H

#include 
#include 

class EmployeeData : public QSharedData {
public:
    EmployeeData() : id(-1) {}
    EmployeeData( const EmployeeData &other ) :
        QSharedData( other ), id( other.id ), name( other.name ) {}

    int id;
    QString name;
};

class Employee {
public:
    Employee() {
        d = new EmployeeData;
    }
    Employee( int id, const QString &name ) {
        d = new EmployeeData;
        d->id = id;
        d->name = name;
    }
    void setId( int id ) {
        d->id = id;
    }
    void setName( const QString &name ) {
        d->name = name;
    }
    int id() const {
        return d->id;
    }
    QString name() const {
        return d->name;
    }

private:
    QSharedDataPointer d;
};

#endif // EMPLOYEE_H

First we should take a look at constructor:

    Employee() {
        d = new EmployeeData;
    }
    Employee( int id, const QString &name ) {
        d = new EmployeeData;
        d->id = id;
        d->name = name;
    }

d = new EmployeeData uses the operator=(T *sharedData) in QSharedDataPointer.
Then for non-const member function of Employee, whenever the d pointer is dereferenced, QSharedDataPointer automatically calls detach() to ensure that the function operates on its own copy of the data. For non-const member, the dereferenced function will call T *operator->().
Oppositely, the const function such as int id() const will use const version const T *operator->() const.

Limitation

  1. Implicit sharing iterator problem.
  2. Threads and implicitly shared classes.

References

  1. http://doc.qt.io/qt-5/containers.html#implicit-sharing-iterator-problem
  2. http://doc.qt.io/qt-5/qshareddatapointer.html

你可能感兴趣的:(Qt Implicit Sharing)