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:
- Define the class Employee to have a single data member of type
QSharedDataPointer<EmployeeData>
.(Shared pointer). - Define the
EmployeeData
class derived fromQSharedData
to contain all the data members you would normally have put in the Employee class.(Actually it is the famousD
pointer)
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
#include <QSharedData>
#include <QString>
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<EmployeeData> 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
.
网友评论